Should I Prefer Pointers or References in Member Data

Should I prefer pointers or references in member data?

Avoid reference members, because they restrict what the implementation of a class can do (including, as you mention, preventing the implementation of an assignment operator) and provide no benefits to what the class can provide.

Example problems:

  • you are forced to initialise the reference in each constructor's initialiser list: there's no way to factor out this initialisation into another function (until C++0x, anyway edit: C++ now has delegating constructors)
  • the reference cannot be rebound or be null. This can be an advantage, but if the code ever needs changing to allow rebinding or for the member to be null, all uses of the member need to change
  • unlike pointer members, references can't easily be replaced by smart pointers or iterators as refactoring might require
  • Whenever a reference is used it looks like value type (. operator etc), but behaves like a pointer (can dangle) - so e.g. Google Style Guide discourages it

When to use references vs. pointers

Use reference wherever you can, pointers wherever you must.

Avoid pointers until you can't.

The reason is that pointers make things harder to follow/read, less safe and far more dangerous manipulations than any other constructs.

So the rule of thumb is to use pointers only if there is no other choice.

For example, returning a pointer to an object is a valid option when the function can return nullptr in some cases and it is assumed it will. That said, a better option would be to use something similar to std::optional (requires C++17; before that, there's boost::optional).

Another example is to use pointers to raw memory for specific memory manipulations. That should be hidden and localized in very narrow parts of the code, to help limit the dangerous parts of the whole code base.

In your example, there is no point in using a pointer as argument because:

  1. if you provide nullptr as the argument, you're going in undefined-behaviour-land;
  2. the reference attribute version doesn't allow (without easy to spot tricks) the problem with 1.
  3. the reference attribute version is simpler to understand for the user: you have to provide a valid object, not something that could be null.

If the behaviour of the function would have to work with or without a given object, then using a pointer as attribute suggests that you can pass nullptr as the argument and it is fine for the function. That's kind of a contract between the user and the implementation.

When is it preferable to store data members as references instead of pointers?

It's only preferable to store references as data members if they're being assigned at construction, and there is truly no reason to ever change them. Since references cannot be reassigned, they are very limited.

In general, I typically store as pointers (or some form of templated smart pointer). This is much more flexible - both for testing (as you mentioned) but also just in terms of normal usage.

Why use references rather than pointers?

I'm not making a point to use one or the other - I'm asking why are references used over pointers to save data members?

Both pointers and references are used as data members. Reference T& is as flexible as immutable pointer T* const. Additionally reference may not be null. So if those properties are describing what you need in the best way then use reference.

IOW, if every Person must have a House and Person may not switch to other House during Person's lifetime then use reference. Apparently both premises feel ridiculous so pointer is actually likely better.

Pointer is not always better. Favor immutability (int* const) to mutability (int*) when things stay immutable. Favor certainty (exactly one) to uncertainty (one or none) when things are certain.

In OOP the composition relation in component object is usually done as reference to composite object. A component can not exist without composite and can not become component of other composite so reference is best.

Should I also pass class member data as references for speed?

The fastest method is to pass values by copy if they fit within the processor's register. The compiler will load the processor's registers then call the function.

For larger data, there may be no speed difference between passing by reference and passing by pointer. In either case, the pointers and references must be deferenced before they can be used, thus taking extra instructions.

Also, one case to measure is to pass structures by copy to the function. This would involve three operations: copy variables to stack, increment stack size, at the receiving side: copying variables from the stack.

The objective to increasing performance is to design the functions to use registers and reduce the amount of loading and storing to memory.

For example, on an ARM7 loading a register from a variable in memory:

  • Load a register with the address of the variable.
  • Load another register by deferencing the register with the address.

    Some processors can load from memory directly by embedding the address within the instruction.

Before coding in the manner, profile your correct and robust code to find the performance bottlenecks.

Is it bad practice to reference class member object from within another member of the same class?

There are some guidelines, but they are generic, like use unique_ptr for owning pointers.

For the rest the guideline is: keep it simple. How did it come to this hierarchy with such an inter-dependency? Could there be a better way to structure your application?

Bidirectional references are possible but you need need to maintain them yourself, or make Outer non-copyable and non-movable:

class Outer {
Some_type member;
std::unique_ptr<Inner> inner;
public:
Outer() {}

Outer(Outer const& that) = delete; // makes Outer not copyable/movable
Outer& operator=(Outer const& that) = delete;

/* The only way to update inner, also sets the back-reference */
void setInner(std::unique_ptr<Inner> ptr) noexcept {
inner = std::move(ptr);
if (inner) {
inner->ptr_to_outer_member = &member;
}
}
};

If Outer is movable, it should actively maintain all back-references to itself to keep them in-sync.

For example like this:

class Some_type {
};
class Inner {
Some_type* ptr_to_outer_member;
friend class Outer; // so that Outer may reach the private ptr_to_outer_member
};

class Outer {
Some_type member;
std::unique_ptr<Inner> inner; // owning pointer to Inner
public:
Outer() noexcept {
}
Outer(Outer&& that) noexcept {
*this = std::move(that);
}
Outer& operator=(Outer&& that) noexcept {
member = std::move(that.member);
setInner(std::move(that.inner));
return *this;
}
/* The only way to update inner, also sets the back-reference */
void setInner(std::unique_ptr<Inner> ptr) noexcept {
inner = std::move(ptr);
if (inner) {
inner->ptr_to_outer_member = &member;
}
}
};

int main() {
Outer o;
o.setInner(std::make_unique<Inner>());
Outer o2(std::move(o)); // OK, move-construction
Outer o3;
o3 = std::move(o2); // OK, move-assignment
Outer o4(o3); // error: Outer is not copyable
}

Having back-references in a movable type almost always requires a custom copy/move-constructor. Which means we have to also keep the rule of 3/5/0 in mind. In this example unique_ptr saves us from having a custom destructor. That may not always be the case.

Why is using a reference or unique pointer member of a class a bad thing?

  1. With regard to a modern C++, does it also mean that using unique_ptr as a class member is usually a bad thing?

It doesn't meant that, and use of unique pointers isn't a bad thing.

Or was it a problem only of auto_ptr and missing move semantics maybe?

The main problem is that copying of auto_ptr transfers ownership of the pointed resource. This is what the author refers to with "peculiar copy semantics".

Given that const auto_ptr cannot be copied, it is not nearly as dangerous as a non-const one is. It had niche uses, but uses of non-copyable types are quite limited pre-C++11.

Unique pointer doesn't have peculiar copy semantics. In fact, unique pointers are not copyable at all. This is not as much of a problem in C++11 where a type can be movable. Unique pointers cover all use cases where auto_ptr was usable, as well as others where auto_ptr was treacherous.


  1. Should shared_ptr be used for a polymorphic behavior then?

Shared pointer can be used, but it is not necessary.

P.S. auto_ptr was deprecated in C++11, and removed from the standard library completely in C++17.

Why should I prefer references on smart pointers over smart pointers as parameters in C++

The possible reasons are:

  1. Performance. It should be faster to pass a reference (one CPU register) rather than a smart pointer by value. There is something wrong with your performance tests.
  2. Saving stack space. A smart-pointer passed by value takes more space on the stack than a reference.

Pointer as member or Reference as member

Avoid using a reference member as much as possible.

The same differences as that of references and pointers apply here,

If you have a reference member then it must be initialized at the time of creation of your class object you cannot have a lazy initialization as in case of pointer member because references cannot be NULL and cannot be reseated, while a pointer member can be made to point to a C2 instance lazily as and when required.

Also, note that there are other side effects as well, Once you have a reference member the compiler will not generate the copy assignment operator(=) & You will have to provide one yourself, It is cubersome to determine what action your = operator shall take in such a case.

For most practical purposes(unless you are really concerned of high memory usage due to C2 size) just holding an instance of C2 as member of C1 should suffice, instead of pointer or reference member, that saves you a whole lot of worrying about other problems which reference/pointer members bring along though at expense of extra memory usage.

If you must, use a pointer make sure you use a smart pointer instead of a raw pointer, that would make your life much easier with pointers.



Related Topics



Leave a reply



Submit