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