C++ 堆栈分配的对象赋值和析构函数调用
Posted
技术标签:
【中文标题】C++ 堆栈分配的对象赋值和析构函数调用【英文标题】:C++ Stack-allocated object assignment and destructor call 【发布时间】:2017-05-07 17:17:06 【问题描述】:我试图了解在为堆栈上分配的对象分配新值时出现的一些奇怪行为(对于同一数据集,析构函数被调用两次)。我将从代码 sn-p 及其输出开始:
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;
输出:
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
我预期会发生什么:
0x...58 在堆栈上创建/初始化 0x...18 在堆栈上创建/初始化 Foo 析构函数在 0x...58 被调用(使用 f1 数据) Foo 析构函数在 0x...18 被调用(使用 f2 数据)实际发生的情况:
0x...58 在堆栈上创建/初始化 0x...18 在堆栈上创建/初始化 来自 0x...18 (f2) 的数据被复制到 0x...58 Foo 析构函数在 0x...18 被调用(使用 f2 数据) Foo 析构函数在 0x...58 处被调用(也带有 f2 数据)所以最后,Foo 析构函数被调用两次以获取相同的数据 (f2)。显然我错过了一些关于它在内部如何工作的东西,所以有人可以指出我正确的方向吗?
【问题讨论】:
您不会看到"f1"
析构函数的消息,因为您复制了一个临时对象,该对象将f
持有的名称从"f1"
更改为"f2"
。因此,您首先会看到临时的 "f2"
破坏,然后是原始实例(现在名为 "f2"
)的破坏。注意数据不一样;它是具有相同值的副本。
如果你的类有析构函数,它几乎肯定也应该有一个拷贝构造函数和赋值运算符。
还有(可能)一个移动构造函数和移动赋值运算符(“五规则”)。
【参考方案1】:
您的实例 f 被分配了 Foo "f2" 的 副本,它不是 新 构造。
添加以下 operator= 覆盖以说明实际发生的情况。
Foo& Foo::operator=(const Foo& other)
cout << "Foo::operator=(const Foo& other)" << endl;
m_name = other.m_name;
return *this;
【讨论】:
【参考方案2】:在创建第二个Foo
对象之前,您在地址0x..58
处只有一个对象。
Address: 0x..58 Data: m_name "f1"
Address: 0x..18 Data: unknown
f = Foo "f2";
行首先创建一个新的 Foo 对象,其m_name
的值为"f2"
,并将其存储在地址0x..18
。然后它将这个对象分配给变量f
。
此分配不会破坏f
中先前存在的对象,它只会将数据成员复制到其中。由于 Foo 对象只有一个数据成员 m_name
,因此赋值只是将第二个对象的 m_name
复制到第一个对象中。
Address: 0x..58 Data: m_name "f2"
Address: 0x..18 Data: m_name "f2"
然后为这些对象中的每一个调用析构函数。输出并不意味着同一个对象被销毁两次,它只是意味着两个对象具有相同的m_name
。
【讨论】:
以上是关于C++ 堆栈分配的对象赋值和析构函数调用的主要内容,如果未能解决你的问题,请参考以下文章