C++中浅拷贝导致的“双重释放”怎么解决?(定义拷贝构造函数实现深拷贝)

10次阅读

浅拷贝引发的双重释放是指类含指针成员且未定义拷贝构造函数时,默认位拷贝使多个对象共享同一内存,析构时多次delete同一地址导致崩溃;须同时实现拷贝构造、拷贝赋值(含自赋值检查)和析构函数,或改用std::vector等标准容器避免手动管理。

C++中浅拷贝导致的“双重释放”怎么解决?(定义拷贝构造函数实现深拷贝)

什么是浅拷贝引发的双重释放

当类里有指针成员且没定义拷贝构造函数时,c++ 默认执行位拷贝(浅拷贝):两个对象的指针成员指向同一块堆内存。一旦其中一个对象析构,delete 了这块内存,另一个对象再析构时再次 delete 同一地址,触发未定义行为——常见表现是程序崩溃、报错 double free or corruption

必须手动定义拷贝构造函数和赋值运算符

只写拷贝构造函数还不够,因为 = 赋值也会触发浅拷贝。必须同时实现:

  • MyClass(const MyClass& other) —— 拷贝构造函数
  • MyClass& operator=(const MyClass& other) —— 赋值运算符(注意自赋值检查)
  • ~MyClass() —— 析构函数中 delete 指针

三者缺一不可,否则仍可能出问题。现代 C++ 推荐用 Rule of Five,但最基础的“三法则”(拷贝构造、拷贝赋值、析构)已能解决该问题。

深拷贝的典型实现要点

核心是为每个对象独立分配并复制数据,而不是共享指针。以含 int* 成员的类为例:

立即学习C++免费学习笔记(深入)”;

class DataHolder { private:     int* data_;     size_t size_; 

public: DataHolder(sizet n) : size(n), data_(new int[n]{}) {}

// 拷贝构造函数:分配新内存 + 逐字节复制 DataHolder(const DataHolder& other)     : size_(other.size_), data_(new int[other.size_]) {     for (size_t i = 0; i < size_; ++i) {         data_[i] = other.data_[i];     } }  // 拷贝赋值:先释放旧资源,再深拷贝(注意自赋值) DataHolder& operator=(const DataHolder& other) {     if (this == &other) return *this;     delete[] data_;     size_ = other.size_;     data_ = new int[other.size_];     for (size_t i = 0; i < size_; ++i) {         data_[i] = other.data_[i];     }     return *this; }  ~DataHolder() { delete[] data_; }

};

关键点:

  • 拷贝构造中不能写 data_(other.data_) —— 这是浅拷贝
  • 赋值运算符必须检查 this == &other,否则 a = a 会先 delete[] 再访问已释放内存
  • new int[n] 就必须配 delete[],混用 delete 会导致未定义行为

更安全的替代方案:优先用标准容器

手动管理裸指针极易出错。绝大多数场景下,直接用 std::vectorstd::String 替代 int*char*

class DataHolder { private:     std::vector data_; 

public: DataHolder(sizet n) : data(n) {}

// 无需自定义拷贝构造、赋值、析构 —— vector 已实现深拷贝语义 // 编译器生成的默认版本完全安全

};

这样既避免双重释放,又省去大量样板代码。只有在性能极端敏感、或必须与 C API 交互时,才考虑手动管理动态内存。

真正容易被忽略的是:即使写了深拷贝,也常忘记同步更新赋值运算符,或者漏掉自赋值检查;而用 std::vector 后,这些风险天然消失。

text=ZqhQzanResources