37

If I interpret C++ references correctly, they are like pointers, but with guaranteed data integrity (no NULL, no (int*) 0x12345). But what happens when scope of referenced object is leaved? If there are no magic involved (and probably it is not), referenced object will be destroyed behind my back.

I wrote a piece of code to check this:

#include <iostream>
using namespace std;

class A {
public:
        A(int k) { _k = k; };
        int get() { return _k; };
        int _k;
};

class B {
public:
        B(A& a) : _a(a) {}
        void b() { cout << _a.get(); }
        A& _a;
};

B* f() {
        A a(10);
        return new B(a);
}

int main() {
        f()->b();
}

The _k instance variable was put in to check stack frame existence.

It surprisingly does not segfault and instead prints '10' correctly, while I suppose that A is allocated on stack and that stack frame of f() will be overwritten by at least cout<< call.

Catherine
  • 22,492
  • 3
  • 32
  • 47

2 Answers2

42

this is undefined behavior and you were simply lucky that the memory for a hadn't been used for anything else yet. In a more complex scenario you would almost certainly get garbage. On my machine I get random garbage with this code. For me, this is likely because I am using a 64-bit machine which uses a register calling convention. Registers get re-used much more often than main memory (ideally...).

So to answer your question of "what happens". Well in this scenario, the reference is likely little more than a limited pointer with friendlier syntax :-). Under the hood the address of a is stored. Later the a object goes out of scope, but the B object's reference to that a will not be "auto-magically" updated to reflect this. Hence you have an invalid reference now.

Using this invalid reference will yield just about anything, sometimes crashes, sometimes just bunk data.


EDIT: Thanks to Omnifarious, I've been thinking about this. There is a rule in c++ that basically says that if you have a const reference to a temporary, then the lifetime of the temporary is at least as long as the const reference. Which introduced a new question.

EDIT: Moved to separate question for those interested (const reference to temporary oddity)

Community
  • 1
  • 1
Evan Teran
  • 87,561
  • 32
  • 179
  • 238
  • Was almost sure about that, but asked just in case C++ has a hidden reference counter somewhere ;) Thanks anyway. – Catherine Jun 22 '10 at 22:30
  • To address that clearly: c++ has no built in reference counting **anywhere**. You get exactly what you ask for and no more. There are however libraries which implement reference counting (such as `boost::shared_ptr` or even `std::tr1::shared_ptr`). – Evan Teran Jun 22 '10 at 22:32
  • 4
    @whitequark: The compiler sort of does compile-time reference counting in some limited cases. If you do something like `A &x = A(10);` you are guaranteed that the `A` object that `x` is referencing will not be destroyed until `x` goes out of scope. That only works if `x` has block scope. It will not work if `x` is a member variable or (I think) a global or static variable. – Omnifarious Jun 22 '10 at 22:44
  • @Omnifarious: a good example of which I expected to see as answer here. – Catherine Jun 22 '10 at 22:46
  • 1
    @Omnifarious: fair enough point, (though I believe it only applies to **`const`** references). That is also not quite the same as reference counting though. But point taken non-the-less. – Evan Teran Jun 22 '10 at 22:56
  • I've started a new question relating to this conversation and my last big edit: http://stackoverflow.com/questions/3097806/const-reference-to-temporary-oddity – Evan Teran Jun 22 '10 at 23:11
  • In order to extend the lifetime of a temporary by attaching a const reference to it, the temporary should be directly used as an initializer for the reference. None of your examples are valid. Passing a temporary through constructor parameters will not work for that purpose. – AnT stands with Russia Jun 22 '10 at 23:19
12

In C++ language the lifetime of the object the reference is bound to is not tied in any way to the lifetime of the reference itself, with only one exception (see below).

If the object is destroyed before the reference is, the reference becomes invalid (like a hanging pointer). Any attempts to access that reference result in undefined behavior. This is what happens in your example.

The the reference ends its lifetime before the object does, then... well, nothing happens. The reference disappears, the object continues to live.

The only exception I was talking above is when a const reference is initialized with an immediate temporary object. In this case the lifetime of the temporary object become tied to the lifetime of the reference. When the reference dies, the object dies with it

{
   const std::string& str = "Hello!"; 
   // A temporary `std::string` object is created here...
   ...
   // ... and it lives as long as the reference lives
   ...
} // ... and it dies here, together with the reference

P.S. You are using the term scope incorrectly. Scope is the visibility region for an identifier. Scope by itself has nothing to do with object lifetime. When something "leaves scope" in general case that something is not destroyed. It is a popular misconception to use the "leaves scope" wording to refer to the destruction point of an object.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 2
    Thanks for pointing the _scope_ misuse -- sometimes trying to map terminology in one language to another produces very weird results -- but this whole question is about things that happen when stack-allocated object gets destroyed when its identifier becames out of scope. (Yeah, the memory is only freed on `leave`, but the destructor should be called anyway, because of that it is destroyed.) – Catherine Jun 23 '10 at 05:02
  • Does the exception include this situation: const T& f(){T t; return t;} const T& tt = f(); use tt here ? Thanks – jean May 28 '13 at 09:22