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

继承与多态场景下内存安全释放问题及代码错误排查

问题分析与解决方案

核心错误原因

1. 构造函数中非法释放未初始化指针

A类的带参构造函数和复制构造函数里,一开始就调用了del_data(),但此时成员变量m_s是未初始化的随机值。del_data()里的if(m_s)判断可能误判为非空,进而调用free(m_s)释放野指针,直接触发内存访问异常。

2. 子类析构函数导致的双重释放

B类显式定义了析构函数并调用A::del_data(),而A类的析构是虚析构,当通过基类指针obj2执行delete时,会先跑B的析构函数,再自动调用A的析构函数。这就导致del_data()被连续调用两次:

  • 第一次在B析构中释放m_s并置为nullptr
  • 第二次在A析构中虽不会重复执行free,但属于完全多余的操作,且后续逻辑变更时容易引发新问题。

修复后的代码

#include <iostream>
#include <cstring>
using namespace std;

class A 
{
private:
    char *m_s = nullptr; // 初始化指针为nullptr,从根源避免野指针
public:
    A() { m_s = strdup("default"); }
    A(char *s) { m_s = strdup(s); } // 移除构造初期不必要的del_data()调用
    A(const A& a) { m_s = strdup(a.m_s); } // 同上
    virtual void prepare() { cout << "A "; }
    void display() {
        prepare();
        cout << m_s << endl;
    }

    void del_data()
    {
        if (m_s)
        {
            free(m_s);
            m_s = nullptr;
        }
    }

    virtual ~A()
    {
        del_data();
    }
};

class B : public A 
{
public:
    B(char *s) : A(s) { }
    B(const B &b) : A(b){}
    void prepare() { cout << "B "; }

    // 移除多余的析构函数,基类虚析构会自动完成资源清理
};

void foo(A *obj1, A obj2) {
    obj1->display();
    obj2.display();
}

int main() {
    B obj1("text");
    A *obj2 = new B(obj1);
    foo(&obj1, *obj2);

    delete obj2; // 现在会正确触发B的析构(空实现),再调用A的析构释放资源

    return 0;
}

避免此类问题的要点

  • 指针成员必须初始化:所有指针成员变量要在声明时或构造函数初始化列表中设为nullptr,杜绝野指针引发的非法内存操作。
  • 构造函数只做初始化:构造函数的职责是创建、初始化资源,不要在构造初期调用释放函数。
  • 子类析构按需定义:如果子类没有新增需要手动释放的资源(比如动态内存、文件句柄),不用显式写析构函数,基类的虚析构会自动处理资源清理。
  • 虚析构的正确使用:当需要通过基类指针删除子类对象时,基类必须声明虚析构,确保析构链(子类→基类)被正确调用。
  • 内存操作严格匹配:strdup分配的内存用free释放,new分配的用delete,new[]对应delete[],绝不混用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 06:30:30