When the c++ compiler generates very similar assembler code for a reference and pointer, why is using references preferred (and considered safer) compared to pointers?
It is a little safer, but not the same thing. Note that you have the same problems of "dangling references" as with "dangling pointers". For instance, returning a reference from a scoped object yields undefined behaviour, exactly the same as pointers:
int& f() { int x = 2; return x; }
The only benefit is that you cannot create a null reference. Even if you try hard:
int& null_ref = *((int*)0); // Dereferencing a null pointer is undefined in C++
// The variable null_ref has an undefined state.
As class members, pointers are preferred since they have better assignment semantics: you cannot reassign a reference when it has been initialized. The compiler won't be able to provide a default assignment operator if there are reference members in the class.
Therefore, C++ cannot get rid of pointers, and you can use them freely: by passing arguments as pointers and not as (non const) references, you make it clear at the call site that the object will be modified. This can add a little safety, since you see by naked eye what functionsindeed modify objects.
I play a little the devil's advocate, but references are easy to abuse.
Because references must always be initialized and since they must refer to an existing object, it is much harder (but by no means impossible) to end up with dangling references than it is to have uninitialized/dangling pointers. Also, it's easier to manipulate references because you don't have to worry about taking addresses and dereferencing them.
But just to show you that a reference by itself doesn't make your program 100% safe, consider this:
int *p = NULL;
int &r = *p;
r = 10; /* bad things will happen here */
Or this:
int &foo() {
int i;
return i;
}
...
int &r = foo();
r = 10; /* again, bad things will happen here */
Because references (which are simply an alias for other variable) can't be NULL
by definition, providing an inherent layer of safety.
Suppose you didn't have the reference operator <type>&
in the language. Then whenever you wanted to pass a reference to an object to a function you would have to do what's done in C, pass the argument as &<myobject>
and receive it in the function as a pointer parameter <type>* p
. Then you would have to discipline yourself not to do something like p++
.
That's where the safety comes in. Passing by reference is very useful, and having the reference operator avoids the risk that you'll modify the pointer.
(Maybe a const pointer would accomplish the same thing, but you've got to admit the &
is cleaner. And it can be applied to other variables besides parameters.)
It's considered safer because a lot of people have "heard" that it's safer and then told others, who now have also "heard" that it's safer.
Not a single person who understands references will tell you that they're any safer than pointers, they have the same flaws and potential to become invalid.
e.g.
#include <vector>
int main(void)
{
std::vector<int> v;
v.resize(1);
int& r = v[0];
r = 5; // ok, reference is valid
v.resize(1000);
r = 6; // BOOM!;
return 0;
}
EDIT: Since there seems to be some confusion about whether a reference is an alias for an object or bound to a memory location, here's the paragraph from the standard (draft 3225, section [basic.life]
) which clearly states that a reference binds to storage and can outlive the object which existed when the reference was created:
If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:
- the storage for the new object exactly overlays the storage location which the original object occupied, and
- the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and
- the type of the original object is not const-qualified, and, if a class type, does not contain any non-static data member whose type is const-qualified or a reference type, and
- the original object was a most derived object of type
T
and the new object is a most derived object of typeT
(that is, they are not base class subobjects).
It depends how you define "safer".
The compiler won't let you create an uninitialised reference, or one that points to NULLness, and it won't let you accidentally make your reference refer someplace else whilst you're using it. These stricter rules also mean that your compiler can give you some more warnings about common mistakes, whereas with pointers it's never really sure whether you meant to do what you did, or not.
On the other hand, the transparency of the syntax -- namely, what Alexandre C. mentioned about what it looks like at the call-site to pass an argument as a reference -- makes it quite easy not to realise that you're passing a reference. Consequently, you might not realise that you're supposed to maintain ownership and lifetime of the argument, or that your argument may get permanently modified.