C++ 函数返回的类上的析构函数
我有以下代码:C++ 函数返回的类上的析构函数,c++,class,g++,destructor,C++,Class,G++,Destructor,我有以下代码: #include <stdio.h> class Foo { public: int a; ~Foo() { printf("Goodbye %d\n", a); } }; Foo newObj() { Foo obj; return obj; } int main() { Foo bar = newObj(); bar.a = 5; bar = newObj(); } 打印的数字似乎是随机的 我
#include <stdio.h>
class Foo {
public:
int a;
~Foo() { printf("Goodbye %d\n", a); }
};
Foo newObj() {
Foo obj;
return obj;
}
int main() {
Foo bar = newObj();
bar.a = 5;
bar = newObj();
}
打印的数字似乎是随机的
我有两个问题:
5
printf
,我很难理解析构函数,何时调用它们以及如何从函数返回类。1)很简单,代码中有两个Foo
对象(在main
和newObj
中),所以有两个析构函数调用。实际上,这是您将看到的最小析构函数调用数,编译器可能会为返回值创建一个未命名的临时对象,如果这样做,您将看到三个析构函数调用。关于返回值优化的规则在C++的历史中发生了变化,所以您可能看到或可能没有看到这种行为。
2) 因为调用析构函数时,Foo::a
的值从来都不是5,在newObj
中它从来都不是5,尽管在main
中它是5,但在到达main
末尾时(也就是调用析构函数时),它已经不是了
我猜你的误解是你认为赋值语句
bar=newObj()
应该调用析构函数,但事实并非如此。在赋值过程中,对象被覆盖,但不会被破坏。让我们看看主函数中发生了什么:
int main() {
Foo bar = newObj();
这里我们只是实例化一个Foo
,并用newObj()的返回值初始化它。这里没有调用析构函数,原因是:为了快速总结,obj
不是复制/移动到bar
然后析构函数obj
,而是直接在bar
的存储中构造obj
bar.a = 5;
这里没什么可说的。我们只需将bar.a
的值更改为5
bar = newObj();
在这里,bar
被复制分配1newObj()
的返回值,然后这个函数调用创建的临时对象被销毁2,这是第一个再见
。此时,bar.a
不再是5
,而是临时对象的a
中的任何内容
}
在main()
的末尾,局部变量被销毁,包括bar
,这是第二个bye
,由于之前的赋值,它不会打印5
1由于用户定义的析构函数,此处不会发生移动分配,因此不会隐式声明移动分配运算符。
2正如YSC在注释中提到的,请注意此析构函数调用具有未定义的行为,因为它正在访问一个此时未初始化的a
。带有临时对象的bar
赋值,特别是作为其一部分的a
赋值,出于同样的原因,也有未定义的行为。我认为这里的主要混淆之一是对象标识
bar
始终是同一个对象。当您将不同的对象分配给bar
时,您不会销毁第一个对象-您可以调用操作符=(const&Foo)
(复制分配操作符)。它是一个可以由编译器自动生成(在本例中就是这样)的函数,只需用newObj()中的任何内容覆盖条.a
。提供您自己的operator=
以查看/何时发生这种情况(并确认a
在发生这种情况之前确实是5
)
bar
的析构函数只调用一次-当函数结束时bar
超出范围时。只有另一个析构函数调用-用于第二个newObj()
返回的临时函数。省略了newObj()
中的第一个临时变量(在这种情况下,语言允许它,因为创建和立即销毁它从来没有真正意义),并首先使用newObj()
的返回值直接初始化bar
,通过初始化a
返回obj避免未定义的行为代码>导致未定义的行为,因为obj.a
未初始化。如果您更改为Foo obj{4}
例如,然后您将看到该值,以及第二次调用中的bar
和obj
析构函数。您还应该从书本或学习资源中学习。C和C++不适合通过试验来弄清规则,这个问题比你想象的更深入。这是因为实际上应该有三个对析构函数的调用:一个是在每次调用newObj
结束并且本地obj
被析构函数时调用;当main
函数结束并且bar
对象被破坏时,会发生一次。如果我们修改程序以在main
函数中打印“checkpoints”(按原样),我们可以看到对newObj
的第一次调用(在定义中Foo bad=newObj();
)不会调用本地对象obj
的析构函数。对newObj
的第二次调用会导致调用析构函数,当bar
被析构函数时,main
的结尾也是如此。这可能是消除对象的无关复制的一部分。技术上有三个Foo
对象:一个在main
函数中,一个在newObj
的每次调用中。我猜只有两个析构函数被调用是因为复制省略(在bar
的复制构造或赋值中),所以实际上只创建了两个对象。@Someprogrammerdude是的,我没有注意到这一点。我想寓意在于,编译器将创建代码,销毁它创建的所有对象,只要您正确定义析构函数,一切都将正常工作。但是创建的对象的数量不是完全可以预测的
}