开发者

How C++ reference works

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 is this instance allocated: when the main function is loaded or when the re开发者_JAVA技巧ference assignment is executed? When it is destroyed?

After the second reference assignment, the object address is not changed. Does this mean that the destructor and constructor are applied to the same memory area? Or is the memory 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;
}

Note from 29.06.2022: After latest edition by Daniel Walker this question looks like complete crap. I am not responsible for this.


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).


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.


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.


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

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


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜