You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

delete ptr与RAII初始化的区别及基类指针删除派生类对象时派生类析构函数未调用的原因

问题解答

一、delete ptr 和 RAII 初始化的区别

先结合你给出的两个场景来拆解核心差异:

  1. RAII 初始化(栈对象):
    你声明的 $PL_STD_MEM::MemDerive derive; 是栈上的局部对象,这正是RAII(资源获取即初始化)的典型应用——对象的生命周期完全由作用域自动管理。当对象离开当前作用域时,编译器会自动按正确顺序调用析构函数(先派生类、后基类),完全不需要手动干预,从根源上避免了忘记释放资源的问题。

    对应的代码与输出:

    $PL_STD_MEM::MemDerive derive;
    derive.printStack();
    derive.printStack(1, 0);
    derive.printStack(1);
    

    输出:
    in namespace std_mem class MemBase constructor: 0
    in namespace std_mem class MemDerive constructor: 1
    in namespace std_mem MemDerive printStack()
    in namespace std_mem MemBase printStack( 1, 0 )
    in namespace std_mem MemBase printStack( true )
    in namespace std_mem class MemDerive destructor: 1
    in namespace std_mem class MemBase destructor: 0

  2. delete ptr(堆对象手动管理):
    你用 new 创建的 $PL_STD_MEM::MemDerive 是堆上的对象,基类指针 memBaseOfDerive 指向它。堆对象的生命周期完全由程序员手动控制,必须显式调用 delete 才能释放内存。它和RAII的核心区别在于:

    • RAII是自动生命周期管理,编译器负责触发析构;delete是手动管理,必须开发者主动调用,否则会造成内存泄漏。
    • RAII通常对应栈对象(也可以结合智能指针实现堆对象的RAII),而new/delete是堆对象的手动管理方式。

二、为什么基类指针删除派生类对象时,派生类的析构函数未被调用?

这是C++中非常经典的非虚析构函数导致的问题:

  • C++中,当通过基类指针删除派生类对象时,只有基类的析构函数是虚函数的情况下,才会触发动态绑定,按先派生类、后基类的顺序调用析构函数。
  • 如果基类的析构函数不是虚函数,delete基类指针时,编译器只会根据指针的静态类型(即MemBase*)调用基类的析构函数,完全忽略派生类的部分——这属于未定义行为(你的例子中表现为只调用基类析构,某些场景下甚至会直接崩溃)。

解决方法很简单:把基类 MemBase 的析构函数声明为虚函数,示例代码如下:

namespace $PL_STD_MEM {
    class MemBase {
    public:
        virtual ~MemBase() {
            cout << "in namespace std_mem class MemBase destructor: 0" << endl;
        }
        // 其他成员函数...
    };
}

修改后再用基类指针删除派生类对象,就会和RAII场景一样,先调用MemDerive的析构函数,再调用MemBase的析构函数。

你的堆对象场景代码与输出:

$PL_STD_MEM::MemBase* memBaseOfDerive;
{
    memBaseOfDerive = new $PL_STD_MEM::MemDerive();
    memBaseOfDerive->printStack();
    memBaseOfDerive->printStack(1, 0);
    memBaseOfDerive->printStack(true);
}
delete memBaseOfDerive;

输出:
in namespace std_mem class MemBase constructor: 0
in namespace std_mem class MemDerive constructor: 1
in namespace std_mem MemDerive printStack()
in namespace std_mem MemBase printStack( 1, 0 )
in namespace std_mem MemBase printStack( true )
in namespace std_mem class MemBase destructor: 0

从输出可以明显看到,派生类的析构日志缺失,这正是基类析构非虚导致的结果。


内容的提问来源于stack exchange,提问作者EggZ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 06:51:12