C++技术问题:析构函数为何在退出作用域前被调用?
为什么
A a = C();会产生额外的析构调用? 问题代码
#include <iostream> using namespace std; class A { public: A() {cout << "A constructor\n";} ~A() {cout << "A deconstructor\n";} }; class B : public A { public: B() {cout << "B constructor\n";} ~B() {cout << "B deconstructor\n";} }; class C : public B { public: C() {cout << "C constructor\n";} ~C() {cout << "C deconstructor\n";} }; int main() { A a = C(); cout << "Exiting scope of main!" << endl; }
实际输出
A constructor
B constructor
C constructor
C deconstructor
B deconstructor
A deconstructor
Exiting scope of main!
A deconstructor
对比指针版本的输出
若将main第一行替换为A* a = new C();,输出为:
A constructor
B constructor
C constructor
Exiting scope of main!
原因分析
1. A a = C();的执行流程
这行代码是拷贝初始化,完整执行步骤如下:
- 创建临时
C对象:按照继承顺序,依次调用A→B→C的构造函数,对应输出前3行构造日志。 - 临时对象初始化
a:这里发生对象切片——a是A类型,只会拷贝临时C对象中属于A的部分,B、C的专属成员被丢弃。编译器会生成默认拷贝构造函数(无输出)完成a的初始化。 - 临时对象销毁:临时
C对象的生命周期在当前表达式结束后立刻结束,按C→B→A的顺序调用析构函数,对应中间3行析构日志。 - 执行
cout语句输出提示信息。 main函数结束:局部变量a(A类型)销毁,调用A的析构函数,对应最后一行日志。
2. A* a = new C();的执行流程
这行代码是创建堆上C对象并让基类指针指向它:
new C()创建堆上C对象,按A→B→C顺序调用构造函数,对应前3行输出。- 指针
a仅保存C对象的地址,不会触发对象切片(对象本身仍是完整的C类型)。 - 由于未编写
delete a;,堆上的C对象不会在main结束前销毁(程序退出时由操作系统回收内存),因此退出main前无析构调用。
关键知识点
- 对象切片:当用派生类对象直接初始化/赋值给基类对象时,基类对象仅保留派生类中的基类部分,派生类专属成员被丢弃;若涉及临时对象,临时对象会在表达式结束后立即销毁。
- 析构多态性:若
A的析构函数不是虚函数,即使基类指针指向派生类对象,调用delete时也只会触发基类的析构函数(本例指针版本未写delete,故未体现)。
内容的提问来源于stack exchange,提问作者vasker
相关产品推荐
相关产品推荐

