Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Stack-allocated object assignment and destructor call

Tags:

c++

I'm trying to understand what appears to be some weird behaviour when assigning a new value to an object allocated on stack (the destructor gets called twice for the same data set). I'll just start with the code snippet and its output:

    class Foo {
    public:
        Foo(const string& name) : m_name(name) {
            log("constructor");
        }
        ~Foo() {
            log("destructor");
        }
        void hello() {
            log("hello");
        }
    private:
        string m_name;
        void log(const string& msg) {
            cout << "Foo." << this << " [" << m_name << "] " << msg << endl;
        }
    };

    int main() {

        {
            Foo f {"f1"};
            f.hello();

            f = Foo {"f2"};
            f.hello();
        }
        cout << "scope end" << endl;
    }

Output:

    Foo.0x7fff58c66a58 [f1] constructor
    Foo.0x7fff58c66a58 [f1] hello
    Foo.0x7fff58c66a18 [f2] constructor
    Foo.0x7fff58c66a18 [f2] destructor
    Foo.0x7fff58c66a58 [f2] hello
    Foo.0x7fff58c66a58 [f2] destructor
    scope end

What I expected to happen:

  • 0x...58 gets created/initialised on stack
  • 0x...18 gets created/initialised on stack
  • Foo destructor gets called on 0x...58 (with f1 data)
  • Foo destructor gets called on 0x...18 (with f2 data)

What actually happens:

  • 0x...58 gets created/initialised on stack
  • 0x...18 gets created/initialised on stack
  • data from 0x...18 (f2) gets copied onto 0x...58
  • Foo destructor gets called on 0x...18 (with f2 data)
  • Foo destructor gets called on 0x...58 (also with f2 data)

So in the end, Foo destructor gets called twice for the same data (f2). Clearly I'm missing something about how this works internally, so can someone please point me in the right direction?

like image 634
dolnub Avatar asked May 07 '17 17:05

dolnub


People also ask

What is stack memory allocation in C?

Stack Allocation : The allocation happens on contiguous blocks of memory. We call it stack memory allocation because the allocation happens in function call stack. The size of memory to be allocated is known to compiler and whenever a function is called, its variables get memory allocated on the stack.

Is it possible to call constructor and destructor explicitly in C++?

- GeeksforGeeks Is it possible to call constructor and destructor explicitly in C++? A constructor is a special member function that is automatically called by the compiler when an object is created and the destructor is also a special member function that is also implicitly called by the compiler when the object goes out of scope.

How is memory allocated and deallocated in C++?

The size of memory to be allocated is known to the compiler and whenever a function is called, its variables get memory allocated on the stack. And whenever the function call is over, the memory for the variables is deallocated. This all happens using some predefined routines in the compiler.

Can destructors be called explicitly as a member function?

Even though a destructor can be called explicitly as a member function, there’s no need to do this. In most cases, where the class data members are dynamically allocated, it can lead to double freeing of the resources. The latter scenario usually yields an abnormal termination of the program.


2 Answers

Your instance f is being assigned a copy of Foo {"f2"}, it's not a new construction.

Add the following operator= override to illustrate what is actually happening.

Foo& Foo::operator=(const Foo& other) {
    cout << "Foo::operator=(const Foo& other)" << endl;
    m_name = other.m_name;  
    return *this;
}
like image 93
RamblinRose Avatar answered Oct 11 '22 11:10

RamblinRose


Before creating the second Foo object, you only have one object at address 0x..58.

Address: 0x..58            Data: { m_name "f1" }
Address: 0x..18            Data: unknown

The line f = Foo {"f2"}; first creates a new Foo object whose m_name value is "f2", and stores it at address 0x..18. Then it assigns this object to the variable f.

This assignment doesn't destroy the previously existing object in f, it only copies the data members into it. Since Foo objects have only one data member, m_name, the assignment just copies the second object's m_name into the first.

Address: 0x..58            Data: { m_name "f2" }
Address: 0x..18            Data: { m_name "f2" }

Then the destructors are called for each of these objects. The output doesn't mean the same object is destroyed twice, it just means that both objects have the same m_name.

like image 39
devil0150 Avatar answered Oct 11 '22 09:10

devil0150