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

什么是浅拷贝引发的双重释放
当类里有指针成员且没定义拷贝构造函数时,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::vector 或 std::string 替代 int* 和 char*:
class DataHolder {
private:
std::vector data_;
public:
DataHolder(sizet n) : data(n) {}
// 无需自定义拷贝构造、赋值、析构 —— vector 已实现深拷贝语义
// 编译器生成的默认版本完全安全
};
这样既避免双重释放,又省去大量样板代码。只有在性能极端敏感、或必须与 C API 交互时,才考虑手动管理动态内存。
真正容易被忽略的是:即使写了深拷贝,也常忘记同步更新赋值运算符,或者漏掉自赋值检查;而用 std::vector 后,这些风险天然消失。









