polymorph源代码:
#include<iostream>// /*structBase{virtualvoidf(){}};structDerived:Base{voidf()override{}};intmain(){Base*p=newDerived;return0;}// */C++ 基类指针删除派生类对象:为什么需要虚析构函数
polymorph.cpp当前启用的代码是:
Base*p=newDerived;目前没有delete p;,所以这里是内存没有释放。如果加上delete p;,确实会产生未定义行为。
关键在于:指针类型是Base*,但它指向的完整对象是Derived。
Base*p=newDerived;// ↑ ↑// 静态类型 动态类型delete p;需要完成两个步骤:
- 析构对象,清理对象管理的资源。
- 释放
new分配的内存。
为了正确销毁这个Derived对象,需要先执行Derived的析构函数,再执行Base的析构函数。虚析构函数提供了通过基类指针找到实际对象析构函数的机制。
你的Base只有:
virtualvoidf(){}这只保证p->f()能调用Derived::f()。每个函数是否为虚函数是分别决定的,f()是虚函数不会让析构函数自动变成虚函数。
编译器隐式生成的Base::~Base()仍然是非虚的。
没有虚析构时,通常会按指针的静态类型选择Base::~Base(),
无法通过这条析构调用完成派生对象的清理。
更复杂的继承结构还可能涉及基类子对象与完整对象的地址不同、释放内存时需要正确的对象大小等问题。
因此,C++ 对这里的普通单对象delete明确规定:通过基类指针删除派生类对象,基类必须具有虚析构函数,否则行为未定义。
要注意,“只调用基类析构函数”只是常见表现,不是标准保证的结果。即使你这里的Derived没有数据成员、没有资源要释放,程序运行起来也正常,仍然违反这条规则。
修改为:
structBase{virtualvoidf(){}virtual~Base()=default;};之后:
Base*p=newDerived;deletep;// 正确:~Derived() → ~Base() → 释放内存Derived的析构函数会自动成为虚函数,无须显式声明。
文件中注释掉的std::unique_ptr<Base>版本也有同样的问题:它的默认删除器最终执行的仍然是对Base*的delete,因此也需要给Base添加虚析构函数。