基类没有虚析构函数,基类指针删除派生类对象,行为是未定义的

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; 需要完成两个步骤:

  1. 析构对象,清理对象管理的资源。
  2. 释放 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 添加虚析构函数。

相关推荐
星夜离尘562 小时前
MATLAB 工程实验:不等间隔流量积分,先把缺口与覆盖率说清楚
开发语言·matlab·时间戳·数据质量·数值积分
Ai-_Man2 小时前
Meta Muse还是不可以导出左侧所有对话,只能点进去单条内容导出,方法不对吗?
开发语言·人工智能·ai·小程序·c#·1024程序员节
梓豪csd2 小时前
消息订阅中心:谁该收到哪条通知
java·开发语言·人工智能·低代码·自动化·1024程序员节
会编程的小孩3 小时前
C++ qt的安装
c++
学逆向的3 小时前
C 枚举变量
c语言·开发语言·网络安全
Sakura_j_h_3 小时前
Python 在定时任务里输出 emoji,UnicodeEncodeError 从哪来
开发语言·字符编码·windows·python·定时任务
小成202303202658 小时前
C++~07补充
开发语言·c++
hold?fish:palm8 小时前
40 二叉树的直径
c++·leetcode
解决小子9 小时前
【虚幻引擎】【C++】【GAS框架】构建角色基类、属性集、数值钳制
c++·笔记·游戏引擎·虚幻·gas·能力系统