tags:

views:

569

answers:

8

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?

+4  A: 

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
I can't even name a case when I would need an assignment of the business object.
Mykola Golubyev
+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
auto_ptr prevents (sensible) assignment too.
anon
@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
Why not just delete in the destructor?
kotlinski
@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
@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.
rstevens
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
I'm in the same case than rstevens, more than half of the classes I manipulate daily are not meant to be assigned.
Klaim
+10  A: 

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
+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.
rstevens
+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
+3  A: 

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
+4  A: 

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

ebo
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
+2  A: 

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
Lakos also arguments for this in "Large-Scale C++ Software Design".
kotlinski
I believe Code Complete advocates this too and I'm not against it. It's not overly compelling though...
markh44
+4  A: 

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)
  • 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
James Hopkin
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
@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
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
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
@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
+8  A: 

My own rule of thumb :

  • Use reference 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. 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!
Klaim
+1: asserts and life time specification.
Mykola Golubyev
I think your's and Mykola's are correct answers and promote pointerless (read less pointer) programming.
Amit Kumar
+8  A: 

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.

anon
Don't quite agree on the first one, but disagreement is not an issue to value the rationale. +1
David Rodríguez - dribeas
+1: I think point 1 is was what I was trying to say too :-)
James Hopkin
(We seem to be losing this argument with the good voters of SO, though)
James Hopkin
Or perhaps the bad voters?
anon
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
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
@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.
anon
@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.
anon
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
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
I agree with Klaim on this.
Amit Kumar