简体   繁体   中英

Is it really the best practice to prefer C++ references to pointers in all situations?

C++ FAQ says:

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

I feel it's not as easy as stated above. The reasons why references are generally better to pointers have been well discussed. So I would rather like to discuss special cases where references make sense but are highly unusual and just don't feel right:

To me the most obvious case is their usage in general data structures like a tree or linked-list. Their interfaces could be certainly implemented using references and feel safer, but I haven't seen a single implementation that would take advantage of references eg:

Node *parent = n.parent(); // parent might be unset -> pointer
Node &child = n.child(5); // child is always valid -> reference

Although it might seem attractive, it leads to code like this:

if(parent == &child) ... //  weird, unusual at least?

I'm wondering, is this what stands for pure C++?

Each time I've tried to blindly use references wherever it's possible, I've encountered all kinds of similar inconsistencies and ended up mixing pointers and references in various ugly ways.

To conclude, why the operator new doesn't return a reference to a new instance instead of a pointer in the first place? (and throws in case of error):

Node &n = new Node;
delete &n;

It's an absurd example, but isn't that what would be “pure C++”?

A reference cannot be modified to refer to a different object once it is initialized. But a pointer could be modified to refer different objects at run time.

A general rule of thumb

Whenever you want a variable to point to different objects during its life time, use a pointer.

If you just want to use a variable as an alias for another variable use a reference.

As parameters

We sometimes pass parameters as references (const/non-const). This would help in avoiding copying the entire object, every time a function is called. Hence this would make much sense, when passing object of large size.

If the function needs to receive some parameter than can optionally be null, you could use a pointer. But, again, you could always define a static null object for any class, and it can be used.

As return values

Whenever we overload operators such as [] or <<, we would return references. It is because, if I say, vec[0] = 10, it should actually assign the value 10 to position 0 in vec. Here I could not not use pointer for obvious reasons. *vec[0] = 10 won't just look nice. Also, you should guarantee, when you are returning a reference it is always referring to an object (It should never be null).

Again, when the function can return NULL values, use a pointer, otherwise you may have to declare a static null object for that class.

As data members

References should be initialized in the constructor. Hence, they cannot be changed once the object is constructed.

Hence if you need a variable that needs to point to different objects during the lifetime of the class, a pointer would be a good choice.

I think generic data structures fall under the "use pointers when you have to" clause. That is, since references are constant (ie they always refer to the same object), using references in your linked list or tree node structure would mean that you couldn't insert new nodes or remove old ones from the data structure--generally defeating the purpose.

As somebody said in the comments, I think that advice really applies to function arguments. Passing references rather than pointers avoids having to add null-pointer handling logic to every function, which brings a host of benefits in terms of code size and clarity.

If you are familiar with NULL and NOT NULL columns in a relational database, or with the difference between a mandatory and an optional element in XSD, you may be able to draw a useful parallel from such a domain to guide you.

References would stand for variables that are fixed, standalone and not nullable. Pointers for those you have a real need sometimes to pass "no value", or where you have a clear need to perform pointer arithmetic (such as with an array). These are by no means the only differences, but most of the different syntax just follows from there.

To recap: reference = NOT NULL and read only. Pointer = flexible and a bit more dangerous.

The "new" operator allocates memory (just like malloc in C). Because C++ does not have a garbage collector implemented (like Java or C#) you can run out of memory. Therefore, when new fails to allocate memory returns a NULL value. This way you can test if the allocation was successful.

You cannot use a reference with the new operator because of the possibility of assigning the NULL value caused by the lack of memory.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM