Reputation: 61
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?
Upvotes: 6
Views: 1500
Reputation: 1469
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
.
Upvotes: 3
Reputation: 4963
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;
}
Upvotes: 4