必须手动实现深拷贝以避免双重释放错误;拷贝构造函数参数为const引用以防递归,赋值运算符需检查自赋值并释放旧资源;推荐用std::vector替代裸指针。

拷贝构造函数必须手动实现深拷贝
默认拷贝构造函数只做位级复制,对 int*、char* 这类原始指针成员,它复制的是地址值而非所指内容。当两个对象析构时,都会调用 delete 释放同一块堆内存,触发双重释放错误(常见报错如 double free or corruption 或直接崩溃)。
解决方式是显式定义拷贝构造函数,在其中为指针成员重新分配内存并逐字节复制内容:
- 参数必须是
const MyArray&—— 否则传值会再次触发拷贝,无限递归导致栈溢出 -
new分配与源对象相同大小的内存,不能复用原指针地址 - 用
for循环或std::copy复制元素,禁用memcpy(它不调用元素的构造函数,对非 POD 类型危险)
赋值运算符重载必须检查自赋值并释放旧资源
仅实现拷贝构造还不够。a = b 调用的是赋值运算符,不是拷贝构造。若未重载,编译器生成的默认版本仍是浅拷贝;若重载但没处理自赋值(a = a),可能先 delete 自己的内存,再试图从已释放内存中拷贝数据。
安全写法要包含三步:释放当前资源 → 深拷贝源对象 → 返回 *this:
- 开头加
if (this == &other) return *this;拦截自赋值 - 释放前先判空(
if (data != nullptr)),避免对空指针delete - 拷贝逻辑与拷贝构造保持一致,避免代码重复(可提取为私有
copy_from(const MyArray&))
用 std::vector 替代裸指针能自动规避问题
如果你只是需要动态数组功能,直接使用 std::vector 是最省心的选择。它的拷贝构造和赋值都默认实现深拷贝,且内部管理内存生命周期,不会出现悬空指针。
裸指针 + 手动 new/delete 的组合,本质是在重复造轮子,且极易遗漏移动语义(C++11 后还需补充移动构造和移动赋值)。除非有特殊性能约束或教学目的,否则没必要自己封装动态数组。
示例对比:
class BadArray {
int* data;
size_t size;
public:
BadArray(size_t s) : size(s) { data = new int[s]; }
// ❌ 没有拷贝构造、没有赋值重载 → 浅拷贝隐患
};
<p>class GoodArray {
std::vector<int> data; // ✅ 自带深拷贝、异常安全、无需手动管理
public:
GoodArray(size_t s) : data(s) {}
};</int></p>
移动构造后必须将源对象指针置为 nullptr
如果类支持移动语义(C++11+),移动构造函数里不能简单地把源对象的指针赋给当前对象,否则源对象析构时仍会 delete 那块内存,造成二次释放。
正确做法是“掏空”源对象:
- 用
std::move转移资源所有权 - 将源对象的指针设为
nullptr,确保其析构函数中if (ptr) delete ptr;不执行 - 移动后源对象处于有效但未指定状态,不可再访问其数据成员
这个步骤容易被忽略——尤其在调试时为了观察地址而故意不置空,上线后就会崩溃。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











