ChatGPT解决这个技术问题 Extra ChatGPT

Should I prefer pointers or references in member data?

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?

"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.
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.

K
Klaim

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.


I think your's and Mykola's are correct answers and promote pointerless (read less pointer) programming.
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.
"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.
J
James Hopkin

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


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: 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).
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.
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.
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.
M
Mykola Golubyev

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.


+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.
+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.
@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).
e
ebo

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


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."
Yes, I don't think this is good advice in the context of member references.
K
Konrad Rudolph

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.


佚名

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.


Don't quite agree on the first one, but disagreement is not an issue to value the rationale. +1
(We seem to be losing this argument with the good voters of SO, though)
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.
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.
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).
p
pts

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


I can't even name a case when I would need an assignment of the business object.
+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)
@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.
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...
I'm in the same case than rstevens, more than half of the classes I manipulate daily are not meant to be assigned.
D
Dani van der Meer

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.


Lakos also arguments for this in "Large-Scale C++ Software Design".
I believe Code Complete advocates this too and I'm not against it. It's not overly compelling though...
S
StephenD

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.