Should I prefer pointers or references in member data?

折月煮酒 提交于 2019-11-26 01:37:19

问题


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?


回答1:


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



回答2:


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.



回答3:


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.




回答4:


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




回答5:


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.




回答6:


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.




回答7:


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



回答8:


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.




回答9:


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.




回答10:


If I understand the question correctly...

Reference as function parameter instead of pointer: As you pointed out, a pointer doesn't make it clear who owns the cleanup/initialization of the pointer. Prefer a shared point when you want a pointer, it's a part of C++ 11, and a weak_ptr when the validity of the data is not guaranteed through the lifetime of the class accepting the pointed to data.; also a part of C++ 11. Using a reference as a function parameter guarantees that the reference is not null. You have to subvert the language features to get around this, and we don't care about loose cannon coders.

Reference a a member variable: As as above in regards to data validity. This guaranties the pointed to data, then referenced, is valid.

Moving responsibility of variable validity to an earlier point in the code not only cleans up the later code (the the class A in your example), but also makes it clear to the person using.

In your example, which is a bit confusing (I'd really try to find a more clearer implementation), the A used by B is guaranteed for the lifetime of the B, since B is a member of A, so a reference reinforces this and is (perhaps) more clear.

And just in case (which is a low likely hood as it wouldn't make any sense in your codes context), another alternative, to use a non referenced, non pointer A parameter, would copy A, and make the paradigm useless - I really don't think you mean this as an alternative.

Also, this also guarantees you aren't able to change the data referenced, where a pointer can be modified. A const pointer would work, only if the referenced/pointer to data was not mutable.

Pointers are useful if the A parameter for B was not guaranteed to be set, or could be reassigned. And sometimes a weak-pointer is just too verbose for the implementation, and a good number of people either don't know what a weak_ptr is, or just dislike them.

Tear apart this answer, please :)



来源:https://stackoverflow.com/questions/892133/should-i-prefer-pointers-or-references-in-member-data

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!