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

C++析构函数删除指针引发野指针问题及解决方案

解决浅拷贝引发的悬垂指针问题

你遇到的是典型的浅拷贝导致的悬垂指针/双重释放问题——类A默认的拷贝构造函数只会逐值复制成员变量,使得原对象和拷贝对象的token指向同一块堆内存。当拷贝对象析构时释放了这块内存,原对象的指针就变成了无效的悬垂指针。下面针对两种场景分别给出解决方案:

场景1:无法修改类A的代码(来自第三方库)

如果不能改动类A的实现,我们只能在调用层面规避拷贝风险,或者手动管理内存来解决问题:

  • 方案1:用智能指针共享对象所有权
    使用std::shared_ptr<A>管理对象,多个智能指针会共享同一个对象的所有权,只有当最后一个智能指针被销毁时才会触发析构,避免提前释放内存。示例代码:
    void someFunction() {
        std::shared_ptr<A> a = std::make_shared<A>();
        cout << a->token;
        // 优先传智能指针,避免拷贝
        anotherFunction(a); 
        cout << a->token;
    }
    // 同时修改函数参数为:void anotherFunction(std::shared_ptr<A> copyOfA)
    
  • 方案2:手动复制内存,保留原指针的有效空间
    如果必须传值拷贝,可在拷贝前复制原token的内存,待拷贝对象析构后重新为原对象分配有效内存(此方式较繁琐,需注意内存泄漏风险):
    void someFunction() {
        A a;
        cout << a.token;
        // 提前复制原token内容
        char* tempToken = new char[strlen(a.token) + 1];
        strcpy(tempToken, a.token);
        anotherFunction(a); // 拷贝对象析构会释放原token内存
        // 重置原对象的token
        delete[] a.token;
        a.token = tempToken;
        cout << a.token;
    }
    
  • 方案3:强制使用引用传递
    你提到过引用传递可避免问题,若无法修改类A,尽量将anotherFunction的参数改为const A& copyOfA或A& copyOfA,这样不会触发拷贝构造,也就不会有析构释放内存的问题。

场景2:可以修改类A的代码

此时我们需要为类A实现深拷贝的拷贝构造函数和赋值运算符重载,确保每次拷贝对象时,都会为token分配独立的内存,而非共享原指针:

实现深拷贝的核心代码

class A {
    char *token;
public:
    A() {
        token = GetRandomToken(); // GetRandomToken返回new分配的char数组
    }
    // 拷贝构造函数:深拷贝
    A(const A& other) {
        size_t len = strlen(other.token) + 1;
        token = new char[len];
        strcpy(token, other.token);
    }
    // 赋值运算符重载:遵循拷贝交换惯用法,更安全可靠
    A& operator=(A other) {
        std::swap(token, other.token);
        return *this;
    }
    ~A() {
        if(token != NULL) {
            delete[] token;
            token = NULL;
        }
    }
};

这样当anotherFunction接收A copyOfA时,会调用自定义的拷贝构造函数,为copyOfA的token分配独立内存,原对象a的token不会被拷贝对象的析构函数影响,也就不会出现悬垂指针了。

额外优化:用std::string替代裸指针

如果允许,把char* token换成std::string token会更省心——std::string本身已实现深拷贝,无需手动管理内存,从根源上避免这类问题:

class A {
    std::string token;
public:
    A() {
        char* temp = GetRandomToken();
        token = temp;
        delete[] temp; // 释放GetRandomToken分配的临时内存
    }
    // 无需手动编写拷贝构造和赋值运算符,编译器生成的即可正常工作
    ~A() {
        // std::string会自动处理内存释放,无需手动操作
    }
};

总结

如果必须拷贝对象:

  • 场景1下,要么用智能指针共享所有权,要么手动复制内存;
  • 场景2下,实现深拷贝的拷贝构造和赋值运算符,或者直接用std::string替代裸指针,彻底规避内存管理风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:17:53