In experimenting with this question I created an example that I utterly do not understand. In particular, it highlights my misunderstanding of pointers, references, and the boost::shared_ptr.
int& r = *(new int(0));//gratuitous pointer leak, got to initialize it to something
{
boost::shared_ptr<int> sp(new int(100));
r = *sp;
cout << "r=" << r << endl;
}
cout << "r=" << r << endl << endl;
int* p;
{
boost::shared_ptr<int> sp(new int(100));
p = &*sp;
cout << "*p=" << *p << endl;
}
cout << "*p=" << *p << endl;
Running this code gives an output something like this:
r=100
r=100
*p=100
*p=13
Why does the reference survive the death of the shared_ptr but the pointer does not?
There's a problem in the answers here in that there seem to be two diametrically opposed and contradictory solutions and no consensus upon which is the truth. I would like the ability to use a reference after a shared_ptr is deleted, but if it's invalid I really need to understand this.
Perhaps someone can post a simple example that demonstrates the undefined behavior in the reference.
Because r = *sp;
does not do what you think it does. It assigns to the referent, that is, to the anonymous int
object you created on the heap in line 1. You cannot reseat references in C++.
Here is what the standard says about evaluating reference expressions:
If an expression initially has the type "reference to T
",
the type is adjusted to T
prior to any further analysis.
The expression designates the object or function denoted by the reference,
and the expression is an lvalue or an xvalue, depending on the expression.
So you see, there is no way to get to "the reference itself". It simply does not exist in C++.
Maybe this code will make it clearer:
int a = 42;
int b = 97;
int&r = a; // r is just an alias (another name) for a
r = b; // assigns b to a (does NOT bind r to b, that's impossible in C++!)
After executing the last line, both a
and b
contain 97, because r = b
really means a = b
.
p is undefined, r is a copy
int& r = *(new int(0));
{
boost::shared_ptr<int> sp(new int(100));
r = *sp; // copy
cout << "r=" << r << endl;
}
cout << "r=" << r << endl << endl;
int* p;
{
boost::shared_ptr<int> sp(new int(100));
p = &*sp;
cout << "*p=" << *p << endl;
}
cout << "*p=" << *p << endl; // Undefined, pointer points to deleted int
In the second case, your int
-object is destructed. In the first case it is not.
In the first case, you create a new int
-object with new
in the outer scope. In the inner scope, you create a second int
-object, for which you also create a shared_ptr
, which then owns the int
-object. This shared_ptr
runs out of scope when you close the inner scope, it therefore gets destructed. The shared_ptr
destructor will also destruct the object it refers to, because no other shared_ptr
(that was created from the original one) refers to your int
object anymore. That's all alright. However, in the middle of that scope you re-assign the value of r
to that of *sp
(100). You therefore save the value of *sp
, before sp
gets destructed, into r
.
Note: it's certainly questionable style to create an int
object the way you do it in your first line of code. If you don't explicitly delete that int
object, this is a memory leek. The way to destruct it would be delete &r
which looks really ugly, especially because the symbol r
afterwards still refers to the, now deleted, int
object. DON'T DO THIS!
In the second case you create an int
pointer at the beginning, but no int
object. The inner scope is almost the same as before, except that this time you do not save the value of your new int
object into the outer-scope variable (p
), but you save the address of the int
object! As the int
object gets destructed at the end of the inner scope (for the same reason as previously), p
no longer points to an existing int
object, but to a place in memory which formerly once hold an int
object. The value you get from *p
is undefined: you might still get 100, you could get any other value, and you might even crash your programme here (Segmentation fault) as you dereference a memory location you no longer hold.
So to summarise, and answer your final question:
The reference survives, because it still refers to an existing object. The pointer does not, because it points to a no longer existing object.