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

C++父类指针删除子类对象时子类析构函数未调用问题

问题根因
  • 核心原因是抽象基类Enemies没有声明虚析构函数。C++标准明确规定:通过基类指针删除派生类对象时,若基类析构函数不是虚函数,会触发未定义行为。你的代码中向量存储的是Enemies*类型指针,实际指向堆上创建的Plant派生类对象,执行delete时因为基类析构非虚,只会调用Enemies类自身的析构函数,完全不会进入Plant类的析构逻辑。

这种场景下的未定义行为不会必然触发程序崩溃,多数编译器会直接执行基类析构逻辑、跳过派生类析构,和你观察到的“对象看似销毁、无非法内存访问报错但存在内存泄漏、Plant析构不触发”的现象完全吻合。

  • 移除逻辑存在隐藏bug:你将待删除元素和向量尾部元素交换、弹出尾部元素后,没有将循环索引i回退1,会导致刚被交换到i位置的元素直接跳过当前轮次的碰撞检测,出现逻辑漏判。
修复方案
  • 第一步(核心修复):给基类Enemies添加虚析构函数。C++11及以上版本可以直接使用默认实现:
class Enemies {
public:
    // 类内原有纯虚接口、成员方法定义
    virtual ~Enemies() = default;
};

添加完成后,通过Enemies*指针删除派生类对象时,会按照「派生类析构→基类析构」的顺序自动执行调用链,Plant类持有的所有资源(比如SDL纹理、投射物管理器引用、动态分配的成员对象等)都会被正确释放。

  • 第二步:简化冗余的手写交换逻辑,补上索引回退的逻辑修正隐藏bug:
// 碰撞检测命中后执行移除
std::swap(m_pEnemies[i], m_pEnemies.back());
delete m_pEnemies.back();
m_pEnemies.pop_back();
i--; // 回退索引,避免漏检刚交换到当前位置的元素
  • (可选优化)如果希望从机制上避免手动管理堆内存的疏漏,可以将向量的存储类型替换为智能指针,不需要手动执行delete:
// 向量类型定义修改为
std::vector<std::unique_ptr<Enemies>> m_pEnemies;

// 插入敌人/植物对象时的写法
m_pEnemies.emplace_back(std::make_unique<Plant>(projectileManager));

// 移除对象时不需要手动释放内存
std::swap(m_pEnemies[i], m_pEnemies.back());
m_pEnemies.pop_back();
i--;

注意:即使使用智能指针管理对象,基类的虚析构函数依然是必须的,否则还是会出现派生类析构不调用、内存泄漏的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 02:09:25