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:
What actually happens:
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?
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.
- 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.
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.
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.
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;
}
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With