155

This is a simplified example to illustrate the question:

class A {};

class B
{
    B(A& a) : a(a) {}
    A& a;
};

class C
{
    C() : b(a) {} 
    A a;
    B b; 
};

So B is responsible for updating a part of C. I ran the code through lint and it whinged about the reference member: lint#1725. This talks about taking care over default copy and assignments which is fair enough, but default copy and assignment is also bad with pointers, so there's little advantage there.

I always try to use references where I can since naked pointers introduce uncertaintly about who is responsible for deleting that pointer. I prefer to embed objects by value but if I need a pointer, I use auto_ptr in the member data of the class that owns the pointer, and pass the object around as a reference.

I would generally only use a pointer in member data when the pointer could be null or could change. Are there any other reasons to prefer pointers over references for data members?

Is it true to say that an object containing a reference should not be assignable, since a reference should not be changed once initialised?

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
markh44
  • 5,804
  • 5
  • 28
  • 33
  • "but default copy and assignment is also bad with pointers": This is not the same: A pointer can be changed whenever you want if it isn't const. A reference is normally always const! (You will see this if you try to change your member to "A& const a;". The compiler (at least GCC) will warn you that the reference is const anyway even without the "const" keyword. – mmmmmmmm May 21 '09 at 10:58
  • 2
    The main problem with this is that if somebody does B b(A()), you are screwed because you are ending up with a dangling reference. – log0 Jan 22 '16 at 23:36

9 Answers9

175

My own rule of thumb :

  • Use a reference member when you want the life of your object to be dependent on the life of other objects : it's an explicit way to say that you don't allow the object to be alive without a valid instance of another class - because of no assignment and the obligation to get the references initialization via the constructor. It's a good way to design your class without assuming anything about it's instance being member or not of another class. You only assume that their lives are directly linked to other instances. It allows you to change later how you use your class instance (with new, as a local instance, as a class member, generated by a memory pool in a manager, etc.)
  • Use pointer in other cases : When you want the member to be changed later, use a pointer or a const pointer to be sure to only read the pointed instance. If that type is supposed to be copyable, you cannot use references anyway. Sometimes you also need to initialize the member after a special function call ( init() for example) and then you simply have no choice but to use a pointer. BUT : use asserts in all your member function to quickly detect wrong pointer state!
  • In cases where you want the object lifetime to be dependent on an external object's lifetime, and you also need that type to be copyable, then use pointer members but reference argument in constructor That way you are indicating on construction that the lifetime of this object depends on the argument's lifetime BUT the implementation use pointers to still be copyable. As long as these members are only changed by copy, and your type don't have a default constructor, the type should fullfil both goals.
Klaim
  • 67,274
  • 36
  • 133
  • 188
  • 1
    I think your's and Mykola's are correct answers and promote pointerless (read less pointer) programming. – amit kumar Aug 13 '09 at 10:40
  • One remark: moved-from objects are destructed using the same destructor as non-moved-from objects (of the same class). Many cases of non-defaulted destructors, require non-defaulted move constructors as well and require some kind of way to distinguish between the moved-from and non-moved-from case. A pointer member variable can be reset to `nullptr` for this purpose, whereas a reference member variable will be preserved. – Matthias Jan 29 '21 at 17:48
  • 2
    "**Use a reference member when you want the life of your object to be dependent on the life of other objects**" is terrible advice. This is not a compile-time dependency. If you want to make sure that the lifetime of object A should depend on object B, use a smart pointer because that guarantees that A will not be destroyed before B is. – Stefan Fabian Mar 04 '22 at 15:22
78

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
James Hopkin
  • 13,797
  • 1
  • 42
  • 71
  • 76
    All things you have mentioned are good things to avoid, so if references helps in this - they are good and not bad. Initializing list is the best place to init the data. Very often you have to hide assignment operator, with references you don't have to. "Cannot be rebound" - good, reuse of variables is a bad idea. – Mykola Golubyev May 21 '09 at 12:33
  • 4
    @Mykola: I agree with you. I prefer members to be initialised in initialiser lists, I avoid null pointers and it's certainly not good for a variable to change its meaning. Where our opinions differ is that I don't need or want the compiler to enforce this for me. It doesn't make classes any easier to write, I've not come across bugs in this area that it would catch, and occasionally I appreciate the flexibility I get from code that consistently uses pointer members (smart pointers where appropriate). – James Hopkin May 21 '09 at 14:22
  • I think not having to hide assignment is a bogus argument since you don't have to hide assignment if the class contains a pointer that it is not responsible for deleting anyway - the default will do. I think this is the best answer because it gives good reasons why pointers should be preferred over references in member data. – markh44 May 22 '09 at 13:05
  • 4
    James, it's not that it makes the code easier to write it's that it makes the code easier to read. With a reference as a data member you never have to wonder if it can be null at point of use. This means that you can look at code with less context required. – Len Holgate Jan 04 '10 at 13:34
  • @Len You're right to focus on readability - after all code is read more than it is written. However, I look at it this way: if I'm reading code and I see p->..., it's clear that p can't be null; if I see if(p), it can be null. It's only when changing the code, that you need to look a little deeper. Note that the case of function parameters is different. Taking a reference parameter prevents accidental passing of null, which I do consider an important consideration. – James Hopkin Jan 04 '10 at 17:16
  • 4
    It makes unit testing and mocking out *much* harder, impossible almost depending on how many are used, combined with 'affect graph explosion' are IMHO compelling enough reasons never to use them. – Chris Huang-Leaver Jan 07 '11 at 11:30
  • Can you elaborate or give a link as to this: "(until C++0x, anyway)" ? – rwst Jul 01 '16 at 06:39
  • @rwst I added a link to a blog post about delegating constructors – James Hopkin Jul 06 '16 at 09:59
  • maybe the green check mark should turn yellow when it has less votes than another answer? I looked at the link to the Google Style Guide and do not agree with the conclusion that it is discouraged. It may be a con, but using references (specifically const) is encouraged. – Eric Roller Jan 09 '19 at 17:17
  • 1
    @EricRoller You're welcome to disagree, but I stand by my answer and I don't feel it would even be controversial to most C++ programmers I've ever worked with. As for whether the tick should be down-graded to yellow: maybe. My main take-away is that StackOverflow isn't a great format for resolving these somewhat subjective questions. – James Hopkin Jan 11 '19 at 13:30
  • If you want to reduce run time, use more references. Why ? To reduce the number of random access. – Ilyes Jun 26 '21 at 01:42
39

Objects rarely should allow assign and other stuff like comparison. If you consider some business model with objects like 'Department', 'Employee', 'Director', it is hard to imagine a case when one employee will be assigned to other.

So for business objects it is very good to describe one-to-one and one-to-many relationships as references and not pointers.

And probably it is OK to describe one-or-zero relationship as a pointer.

So no 'we can't assign' then factor.
A lot of programmers just get used with pointers and that's why they will find any argument to avoid use of reference.

Having a pointer as a member will force you or member of your team to check the pointer again and again before use, with "just in case" comment. If a pointer can be zero then pointer probably is used as kind of flag, which is bad, as every object have to play its own role.

Mykola Golubyev
  • 57,943
  • 15
  • 89
  • 102
  • 2
    +1: Same I experienced. Only some generic data storage classes need assignemtn and copy-c'tor. And for more high-level business object frameworks there should be other techniques for copying that also handle stuff like "do not copy unique fields" and "add to another parent" etc. So you use the high-level framework for copying your business objects and disallow the low-level assignments. – mmmmmmmm May 21 '09 at 11:06
  • 2
    +1: Some points of agreement: reference members preventing an assignment operator being defined is not an important argument. As you correctly state a different way, not all objects have value semantics. I also agree that we don't want 'if (p)' scattered all over the code for no logical reason. But the correct approach to that is through class invariants: a well-defined class will leave no room for doubt about whether members can be null. If any pointer can be null in the code, I'd expect that to be well commented. – James Hopkin May 21 '09 at 14:38
  • @JamesHopkin I agree that well-designed classes can use pointers effectively. Besides guarding against NULL, references also guarantee that your reference was initialized (a pointer doesn't need to be initialized, so it could point to anything else). References *also* tell you about ownership--namely that the object doesn't own the member. If you consistently use references for aggregate members, you'll have a very high degree of confidence that pointer members are composite objects (though there aren't many cases where a composite member is represented by a pointer). – weberc2 Apr 25 '15 at 15:16
11

Use references when you can, and pointers when you have to.

ebo
  • 8,985
  • 3
  • 31
  • 37
  • 8
    I had read that but I figured it was talking about passing parameters, not member data. "References typically appear on the skin of an object, and pointers on the inside." – markh44 May 21 '09 at 10:03
  • 2
    Yes, I don't think this is good advice in the context of member references. – Tim Angus Jan 30 '17 at 14:27
7

In a few important cases, assignability is simply not needed. These are often lightweight algorithm wrappers that facilitate calculation without leaving the scope. Such objects are prime candidates for reference members since you can be sure that they always hold a valid reference and never need to be copied.

In such cases, make sure to make the assignment operator (and often also the copy constructor) non-usable (by inheriting from boost::noncopyable or declaring them private).

However, as user pts already commented, the same is not true for most other objects. Here, using reference members can be a huge problem and should generally be avoided.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
5

As everyone seems to be handing out general rules, I'll offer two:

  • Never, ever use use references as class members. I have never done so in my own code (except to prove to myself that I was right in this rule) and cannot imagine a case where I would do so. The semantics are too confusing, and it's really not what references were designed for.

  • Always, always, use references when passing parameters to functions, except for the basic types, or when the algorithm requires a copy.

These rules are simple, and have stood me in good stead. I leave making rules on using smart pointers (but please, not auto_ptr) as class members to others.

  • 2
    Don't quite agree on the first one, but disagreement is not an issue to value the rationale. +1 – David Rodríguez - dribeas May 21 '09 at 11:16
  • (We seem to be losing this argument with the good voters of SO, though) – James Hopkin May 21 '09 at 11:32
  • 2
    I voted down because "Never, ever use references as class members" and the following justification is not right to me. As explained in my answer (and comment on the top answer) and from my own experience, there are cases where it's simply a good thing. I thought like you until I was hired in a company where they were using it in a good way and it made it clear to me that there are cases where it helps. In fact I think the real problem is more about the syntax and hidden implication that makes usage of references as members require the programmer to understand what he's doing. – Klaim May 21 '09 at 12:28
  • The choice is between pointer and reference. Of course shared pointers are the best choice to reflect relationships. Could you expand your 'semantic are too confusing'? – Mykola Golubyev May 21 '09 at 12:28
  • @Klaim - it's a good idea not to vote down opinions, particularly when you are posting your own opinions as answers to the same question. –  May 21 '09 at 12:31
  • @mykola - the fact that references cannot be reseated and thus prevent assignment is what I meant. Most people simply do not think "reference" == "cannot assign". Unlike you, I think it is (rarely) desirable to be able to copy business objects. And it is _very_ desirable to copy non-business objects. –  May 21 '09 at 12:34
  • 1
    Neil> I agree but it's not "opinion" that made me vote down, it's the "never" assertion. As arguing here don't seem to be helpful anyway i'll cancel my down vote. – Klaim May 21 '09 at 14:23
  • Having thought about it I agree with Neil's answer - it is much better to keep things simple and avoid confusion instead of playing around with subtleties that not many people will notice or fully understand. Assigning classes with references is possible but you need to ensure that the reference in the lhs already refers to the same object. If it doesn't, what do you do? Not alot - you're stuck with a broken assignment. And you're right, it's confusing as hell. – markh44 May 22 '09 at 15:17
  • 2
    This answer could be improved by explaining what about reference member semantics are confusing. This rationale is important because, on its face, pointers seem more semantically ambiguous (they may not be initialized, and they tell you nothing about ownership of the underlying object). – weberc2 Apr 25 '15 at 15:09
4

Yes to: Is it true to say that an object containing a reference should not be assignable, since a reference should not be changed once initialised?

My rules of thumb for data members:

  • never use a reference, because it prevents assignment
  • if your class is responsible for deleting, use boost's scoped_ptr (which is safer than an auto_ptr)
  • otherwise, use a pointer or const pointer
pts
  • 80,836
  • 20
  • 110
  • 183
  • 3
    I can't even name a case when I would need an assignment of the business object. – Mykola Golubyev May 21 '09 at 10:07
  • 1
    +1: I'd just add to be careful with auto_ptr members - any class that has them must have assignment and copy construction explicitly defined (the generated ones are very unlikely to be what's required) – James Hopkin May 21 '09 at 10:14
  • auto_ptr prevents (sensible) assignment too. –  May 21 '09 at 10:36
  • @Neil - I was going to comment the same thing, but actually I could imagine writing, say, std::string using an auto_ptr to the char buffer. Assignment would use the existing buffer or allocate a new one as appropriate. auto_ptr would provide some help with exception safety. Having said that, using a boost::scoped_ptr equivalent would be less error prone than using auto_ptr. – James Hopkin May 21 '09 at 10:45
  • Why not just delete in the destructor? – Johan Kotlinski May 21 '09 at 10:50
  • @kotlinski - consider if an exception is thrown during construction after an object has been newed and assigned to the pointer member. This possibility is there in any class that allocates more than one object in its constructor. – James Hopkin May 21 '09 at 11:01
  • 3
    @Mykola: I also made the experience that 98% of my business objects do not need assignment or copy-c'tors. I prevent this by a small macro that I add to the headers of those classes which makes copy c'tors and operator=() private with no implementation. So in the 98% of the objects I also use references and it is safe. – mmmmmmmm May 21 '09 at 11:02
  • 2
    References as members can be used to explicitely state that the life of the instance of the class is directly dependent on the life of instances of other classes (or the same). "Never use a reference, because it prevents assignment" is assuming that all classes have to allow assignment. It is like assuming that all classes have to allow copy operation... – Klaim May 21 '09 at 11:03
  • 1
    I'm in the same case than rstevens, more than half of the classes I manipulate daily are not meant to be assigned. – Klaim May 21 '09 at 11:04
2

I advise against reference data members becasue you never know who is going to derive from your class and what they might want to do. They might not want to make use of the referenced object, but being a reference you have forced them to provide a valid object. I've done this to myself enough to stop using reference data members.

StephenD
  • 231
  • 1
  • 10
2

I would generally only use a pointer in member data when the pointer could be null or could change. Are there any other reasons to prefer pointers over references for data members?

Yes. Readability of your code. A pointer makes it more obvious that the member is a reference (ironically :)), and not a contained object, because when you use it you have to de-reference it. I know some people think that is old fashioned, but I still think that it simply prevent confusion and mistakes.

Dani van der Meer
  • 6,169
  • 3
  • 26
  • 45