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

VERIFY试验中结构体浅拷贝导致指针未同步置空问题求助

解决C++结构体浅拷贝导致的双删与野指针问题

嘿,我来帮你搞定这个坑!你遇到的正是C++里经典的浅拷贝陷阱——默认的赋值运算符和拷贝构造函数只会逐成员复制指针的地址,而不是复制指针指向的堆内存内容,这就导致两个Verify对象共享同一块内存,析构时会重复释放,还会留下野指针,完全不符合你预期的行为。

问题根源分析

当你执行hispoint = mypoint;时,默认赋值运算符直接把mypoint.ptr的内存地址拷贝给了hispoint.ptr,两个指针指向同一块new int(15)的堆内存。

当main函数作用域结束时:

  1. 先析构hispoint:调用DEL函数释放堆内存,并把hispoint.ptr置空,但这不会影响mypoint.ptr的值——它还是原来的内存地址,现在已经变成了野指针(指向已释放的内存)。
  2. 接着析构mypoint:DEL函数检测到mypoint.ptr不为空,试图再次释放已经被释放的内存,直接触发未定义行为(通常是程序崩溃或内存错误)。

三种可行解决方案

1. 手动实现深拷贝的拷贝构造与赋值运算符

如果必须使用裸指针,就需要自己实现深拷贝逻辑,让每个对象拥有独立的堆内存:

#include "stdafx.h"
#include "Precompiled.h"
#include <crtdbg.h>
#define ASSERT _ASSERTE
#ifdef _DEBUG
#define VERIFY ASSERT
#else
#define VERIFY(expression) (expression)
#endif

struct Verify {
    int* ptr;

    // 默认构造函数,初始化指针为空
    Verify() : ptr(nullptr) {}

    // 拷贝构造函数:深拷贝对方的内存
    Verify(const Verify& other) {
        if (other.ptr != nullptr) {
            ptr = new int(*other.ptr); // 复制指针指向的内容,而非地址
        } else {
            ptr = nullptr;
        }
    }

    // 赋值运算符:深拷贝+自赋值保护
    Verify& operator=(const Verify& other) {
        if (this != &other) { // 防止自赋值导致的内存泄漏
            // 先释放当前对象的内存
            if (ptr != nullptr) {
                delete ptr;
                ptr = nullptr;
            }
            // 深拷贝对方的内容
            if (other.ptr != nullptr) {
                ptr = new int(*other.ptr);
            }
        }
        return *this;
    }

    ~Verify() {
        VERIFY(DEL(&ptr));
    }

    bool DEL(int** p) {
        if (*p != nullptr) {
            delete *p;
            *p = nullptr;
            return true;
        } else {
            return false;
        }
    }
};

auto main() -> int {
    Verify mypoint;
    Verify hispoint;
    mypoint.ptr = new int(15);
    hispoint = mypoint; // 现在是深拷贝,两个对象各有独立的内存
}

2. 使用智能指针(推荐的现代C++方式)

用std::unique_ptr或std::shared_ptr代替裸指针,智能指针会自动管理内存,从根源上避免浅拷贝问题:

#include "stdafx.h"
#include "Precompiled.h"
#include <crtdbg.h>
#include <memory> // 必须包含的头文件
#define ASSERT _ASSERTE
#ifdef _DEBUG
#define VERIFY ASSERT
#else
#define VERIFY(expression) (expression)
#endif

struct Verify {
    // 使用独占所有权的unique_ptr,禁止拷贝,只能移动
    std::unique_ptr<int> ptr;

    ~Verify() {
        // 无需手动写DEL,unique_ptr会自动释放内存
        VERIFY(ptr == nullptr || true);
    }
};

auto main() -> int {
    Verify mypoint;
    Verify hispoint;
    mypoint.ptr = std::make_unique<int>(15);
    // 用std::move转移所有权,此时mypoint.ptr变为nullptr,hispoint拥有内存
    hispoint.ptr = std::move(mypoint.ptr);
}

如果需要多个对象共享内存,可以把std::unique_ptr换成std::shared_ptr,它会通过引用计数管理内存释放时机。

3. 禁用拷贝与赋值(如果不需要拷贝功能)

如果你的Verify对象本来就不应该被拷贝,可以直接禁用拷贝构造函数和赋值运算符,编译器会提前报错阻止错误操作:

struct Verify {
    int* ptr;

    // 显式禁用拷贝和赋值
    Verify(const Verify&) = delete;
    Verify& operator=(const Verify&) = delete;

    ~Verify() {
        VERIFY(DEL(&ptr));
    }

    bool DEL(int** p) {
        if (*p != nullptr) {
            delete *p;
            *p = nullptr;
            return true;
        } else {
            return false;
        }
    }
};

此时如果再写hispoint = mypoint;,编译器会直接抛出错误,避免你不小心触发浅拷贝。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:47:57