函数返回的类上的析构函数
Destructors on classes returned by functions
我有以下代码:
#include <stdio.h>
class Foo {
public:
int a;
~Foo() { printf("Goodbye %dn", a); }
};
Foo newObj() {
Foo obj;
return obj;
}
int main() {
Foo bar = newObj();
bar.a = 5;
bar = newObj();
}
当我使用g++
编译并运行它时,我得到:
Goodbye 32765
Goodbye 32765
打印的数字似乎是随机的。
我有两个问题:
- 为什么析构函数被调用两次?
- 为什么第一次没有打印
5
?
我来自 C 背景,因此printf
,我很难理解析构函数、何时调用它们以及如何从函数返回类。
让我们看看你的主函数会发生什么:
int main() {
Foo bar = newObj();
在这里,我们只是实例化一个Foo
并使用返回值newObj()
初始化它。这里没有因为复制省略而调用析构函数:总结得非常快,而不是将obj
复制/移动到bar
然后销毁obj
,obj
直接构造在bar
的存储中。
bar.a = 5;
这里没什么好说的。我们只是将bar.a
的值更改为 5。
bar = newObj();
这里bar
被复制赋值1的返回值newObj()
,那么这个函数调用创建的临时对象被破坏2,这是第一个Goodbye
。此时bar.a
不再是5
而是临时对象a
中的任何内容。
}
main()
结束时,局部变量被破坏,包括bar
,这是第二个Goodbye
,由于之前的赋值而没有打印5
。
1由于用户定义的析构函数,此处不会发生移动赋值,也不会隐式声明移动赋值运算符。
2正如 YSC 在评论中提到的,请注意,此析构函数调用具有未定义的行为,因为它正在访问此时未初始化的a
。出于同样的原因,将bar
与临时对象一起分配,特别是将a
作为临时对象的一部分进行分配,也具有未定义的行为。
1)很简单,代码中有两个Foo
对象(main
和newObj
),所以有两个析构函数调用。实际上,这是您将看到的析构函数调用的最小数量,编译器可能会为返回值创建一个未命名的临时对象,如果这样做了,您将看到三个析构函数调用。在C++的历史记录中,返回值优化的规则已发生变化,因此您可能会也可能看不到此行为。
2)因为当析构函数被调用时,Foo::a
的值永远不会是5,它在newObj
中永远不会是5,虽然它在main
是5,但当你到达main
结束时(也就是调用析构函数的时候)。
我猜你的误解是你认为赋值语句bar = newObj();
应该调用析构函数,但事实并非如此。在分配期间,对象被覆盖,它不会被销毁。
我认为这里的主要混淆之一是对象标识。
bar
始终是同一个对象。当您将不同的对象分配给bar
时,您不会销毁第一个对象 - 您调用operator=(const& Foo)
(复制赋值运算符)。它是编译器可以自动生成的五个特殊成员函数之一(在这种情况下是这样),并且只是用newObj().a
中的任何内容覆盖bar.a
。提供您自己的operator=
以查看/何时发生这种情况(并确认a
确实5
发生这种情况)。
bar
的析构函数只被调用一次 - 当bar
在函数结束时超出范围时。只有一个其他析构函数调用 - 用于第二个newObj()
返回的临时。从newObj()
中的第一个临时被省略(语言在这种情况下允许它,因为从来没有真正意义上的创建和立即销毁它)并使用返回值newObj()
直接初始化bar
。
- 什么时候调用组成单元对象的析构函数
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 内联映射初始化的动态atexit析构函数崩溃
- 什么时候调用析构函数
- 优先顺序:智能指针和类析构函数
- C++-明确何时以及如何调用析构函数
- 使用基类指针创建对象时,缺少派生类析构函数
- 在c++中使用向量时,如何调用构造函数和析构函数
- 重载运算符new[]的行为取决于析构函数
- 我需要知道编译器如何在cpp中使用析构函数
- 为什么在使用转换构造函数赋值后调用C++类的析构函数?
- 析构函数调用
- 通过引用传递-为什么要调用这个析构函数
- 对具有动态分配的内存和析构函数的类对象的引用
- 重载 -> shared_ptr 个实例中的箭头运算符<interface>,接口中没有纯虚拟析构函数
- C++成员的析构函数顺序与shared_ptr
- C++ 防止在映射中放置()时调用析构函数
- 在这种情况下显式调用时,std::cout 如何更改析构函数的行为?
- 调用析构函数以释放动态分配的内存
- 不命名构造函数和析构函数上的类型错误