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

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 添加虚析构函数。

相关推荐
迅猛龙办公室1 小时前
python获取星期字符串
开发语言·python
xlq223221 小时前
全面复习2
c++
无名猿1 小时前
移动构造与移动赋值:把资源偷过来
c++·性能优化·内存管理·现代c++·语法基础
泡海椒2 小时前
JQuick-Excel dateFormat 转换实战:日期值与 FORMAT 显示格式的边界
开发语言·python·excel
All for pursuit.2 小时前
【回溯-7】494.目标和
数据结构·c++·算法·leetcode
白杨尚青2 小时前
C++入门篇(十三):vector(上)——动态数组:构造、空间增长与迭代器失效
开发语言·c++·笔记·算法·stl
江江爱编程2 小时前
java零基础保姆级教学01
java·开发语言
沐晓时光3 小时前
C语言入门,深入理解指针(2)
c语言·开发语言
2601_962885723 小时前
如何用 Python 做均线粘合选股?(多头发散前的变盘候选)
开发语言·python