我是 C++ 的新手,我写了一个小程序来了解赋值是如何处理对象的。我被提示从本页 ( http://www.cplusplus.com/doc/tutorial/classes2/ )的 cpp 文档中执行此操作。在此页面上,它指出:
[复制赋值运算符的]隐式版本执行适用于许多类的浅拷贝,但不适用于具有指向它们处理其存储的对象的指针的类。在这种情况下,不仅类会招致两次删除所指向对象的风险,而且由于在赋值之前不删除对象所指向的对象,赋值会造成内存泄漏。
我用粗体格式化的最后一部分是我决定测试的原因。我认为这个问题可以通过在析构函数中处理指向对象的删除来解决(这是标准的?),而不必重载复制赋值运算符。如果没有调用析构函数,那不是真的很不方便吗?假设我有多个引用的对象,我必须将所有删除都放在析构函数(对于大多数重新分配的情况)和赋值重载中。
在这次测试中,我遇到了一个完全不同的问题。我最初的想法是创建一个简单的类,它存储一个 int(作为测试目的的标识符)并重载构造函数和析构函数以查看何时以及是否调用析构函数。
这是我的代码:
class Test{
public:
int id;
explicit Test(int id) : id(id) {
cout << "Created " << id << endl;
}
~Test() {
cout << "Destroyed " << id << endl;
}
};
int main() {
Test x = Test(1);
x = Test(2);
cout << x.id << endl;
return 0;
}
我本来期望的输出是:
1:Created 1
2 Destroyed 1
:?(这是我不确定的那个,因为网站暗示如果对象被另一个“替换”而不是超出范围,则不会调用此析构函数)
3:Created 2
对象 2“替换”对象 1,因为它被分配给 x
4:2
打印出的对象 2 的 id 值
5:Destroyed 2
对象 2 超出范围时被销毁
相反,我得到了以下输出:
Created 1
Created 2
Destroyed 2
2
Destroyed 2
这对我来说真的没有意义。
使用调试器,Created 2
并且Destroyed 2
在x = Test(2);
调用该行时都显示。如果我们只是分配x
给对象 2,为什么会立即调用它的析构函数?这是下一部分。
其次,由于对象 2 的析构函数已被调用,我们可以假设它已被销毁。的下一个输出2
似乎与此相矛盾,因为它表明它x
仍然持有对象 2(预期,但与其析构函数的调用相矛盾)。
我不太确定为什么会这样。
最后,Destroyed 2
输出。如果我们没有更早地看到这一点,这将是有道理的。对象 2 存储在 中x
,因此当它超出范围时会调用析构函数。
出于某种原因,我们调用了两次析构函数,对象 1 被对象 2 的赋值“覆盖”,x
从不调用其析构函数,而是我们刚刚创建的对象的析构函数调用了其析构函数。
所以......这总结了一个由两部分组成的问题:
1:为什么会发生这种奇怪的行为,是否有任何合乎逻辑的原因?
2:通过赋值用另一个对象(对象2)“覆盖”一个对象(例如对象1)是否会导致其析构函数(在这种情况下是对象1的析构函数)被调用?
提前致谢。
使用调试器,当行 x = Test(2); 时,Created 2 和 Destroyed 2 都会显示;叫做。如果我们只是将 x 分配给对象 2,为什么会立即调用它的析构函数?这是下一部分。
该行x = Test(2);
首先Test
使用构造函数参数创建 a 2
。这就是产生Created 2
. Test
然后将这个无名分配给x
它,给出x.id
值 2。Test
然后在表达式的末尾销毁无名,产生“Destroyed 2”。
其次,由于对象 2 的析构函数已被调用,我们可以假设它已被销毁。2 的下一个输出似乎与此矛盾,因为它表明 x 仍然持有对象 2(预期,但与其析构函数的调用相矛盾)。
正如这个答案的第一部分所表达的那样,它不是x
被破坏,而是临时的Temp
. x.id
仍然有效并将产生它的新值 2。
最后输出Destroyed 2。如果我们没有更早地看到这一点,这将是有道理的。对象 2 存储在 x 中,因此当它超出范围时会调用析构函数。
当x
在函数结束时销毁时会发生这种情况。它的id
值被之前的赋值更改为 2,因此它产生“Destroyed 2”。
1:为什么会发生这种奇怪的行为,是否有任何合乎逻辑的原因?
这可能不是您期望的行为,但并不奇怪。我希望这个答案能帮助你理解它为什么会发生。
2:通过赋值用另一个对象(对象 2)“覆盖”一个对象(例如对象 1)是否会导致其析构函数(在本例中为对象 1 的析构函数)被调用?
分配给一个对象不会破坏它。它用一个新的值替换它的值,从这个意义上说,它“破坏”了它以前帮助的值,但实际的对象实例没有被破坏,也不涉及析构函数。
编辑:看来您可能担心资源泄漏。由于没有资源被管理,Test
不会有泄漏,编译器生成的成员将表现良好。如果您的类确实管理资源(通常以动态分配内存的形式),那么您将需要应用3/5/0 规则。值得注意的是,您需要自己实现赋值运算符,以便它清理任何以前持有的资源。仅仅实现析构函数是不够的,因为它不涉及赋值。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句