3
votes

After working 15 years in C++ I found that I don't understand references completely...

class TestClass
{
public:
    TestClass() : m_nData(0)
    {
    }

    TestClass(int n) : m_nData(n)
    {
    }

    ~TestClass()
    {
        cout << "destructor" << endl;
    }

    void Dump()
    {
        cout << "data = " << m_nData << "  ptr = 0x" << hex << this << dec << endl;
    }

private:
    int m_nData;
};

int main()
{
    cout << "main started" << endl;

    TestClass& c = TestClass();
    c.Dump();

    c = TestClass(10);
    c.Dump();

    cout << "main ended" << endl;

    return 0;
}

// prints:
// main started
// data = 0  ptr = 0x0012FF54
// destructor
// data = 10  ptr = 0x0012FF54
// main ended
// destructor

I understand from this test, that TestClass instance is created on the stack (is this correct?) and initialized by first TestClass constructor. When this instance is allocated: when main function is loaded, or reference assignment is executed? When it is destroyed?

After second reference assignment object address is not changed. Does this mean that destructor and constructor are applied to the same memory area? Or memory is deallocated (dynamically? on the stack?) and allocated again?

I know everything about stack and heap-allocated objects lifetime, their constructors and destructors. But I cannot understand what exactly happens in this program.

Edit: Thanks to all. I tried to reproduce in this test some other (more complicated) program behavior. Your comments helped me to understand both my mistake and another program I am fighting with...

Fixed code is:

int main()
{
    cout << "main started" << endl;
    TestClass t;

    TestClass& c(t);
    c.Dump();

    c = TestClass(10);
    c.Dump();

    cout << "main ended" << endl;
    return 0;
}
5
This code won't compile. You can't bind a non-const reference to a temporary.Oliver Charlesworth
Are there any implementations of C++ that don't put automatic objects on a stack? If so, which ones are they?Jeremy Friesner
Which compiler did you use to compile that? It shouldn't compile.Cheers and hth. - Alf
@Jeremy: there are none, it's logically impossible. the puzzling thing is that those who propagate that urban myth are not all idiots. i have no explanation of why they do this, other than maybe it's like the scotsman who was sentenced for having sex with the pavement, it's just inexplicable behavior.Cheers and hth. - Alf
Please keep comments constructive and on topic.Tim Post♦

5 Answers

5
votes

Your code suffers from multiple problems and ultimately won't make sense. However, let's hack through it.

1) You can only bind a temporary to a const reference, thus extending its lifetime:

const TestClass & c = TestClass();

2) Now we can't use dump, because you didn't declare it const:

void Dump() const

3) Saying c = TestClass() is an assignment. However, c is now a reference-to-const, which cannot be assigned to, since assignment is non-constant (for obvious reasons). Let's hack around this:

const_cast<TestClass&>(c) = TestClass(10);

Now we've assigned a new value to the temporary-but-extended object c, and all is as it should be:

main started
data = 0  ptr = 0x0xbfa8219c
destructor
data = 10  ptr = 0x0xbfa8219c
main ended
destructor

The pointers are the same because there's only one object, namely the (temporary) one referenced by c. Assigning to it is a hack that's undefined behaviour in general, but we get away with it for the purpose of this demonstration.

The intermediate destructor is that of the second temporary TestClass(10).

2
votes
TestClass& c = TestClass(); // TestClass() temporary doesn't persist beyond this expression.
c.Dump();

TestClass() creates a temporary and you cannot take the reference of it.

const TestClass& c = TestClass();

const qualification extends the life time of the temporary being created until the scope of the object c.

2
votes
TestClass& c = TestClass();

This wouldn't even compile!

Attempting to bind a temporary to non-const reference would result in compilation error.

However, you can bind a temporary to const reference:

{
   const TestClass& c = TestClass();
   //use c 
   //....
}//<-------- the temporary will be destroyed here.

In this case, the life of the temporary extends to the lifetime of the reference, i.e when the reference variable goes out of scope, the temporary will be destroyed as shown above.

2
votes

1) you can't get not const reference to a temporary object

2) in the line c = TestClass(10); operator=(...) is called

1
votes

A good way is to compare references to pointers... (references are usually implemented the same way in assembly generally by using the ebx register). The main difference is that reference is constant after initialization...

However, The line const TestClass& c = TestClass(); is parallel to const TestClass* const pc = &TestClass(); so the object will be create and destroyed on the stack, pc will still hold the same address.