深拷贝指针成员必须在拷贝构造函数中用new重新分配内存并复制内容,否则默认浅拷贝会导致析构后悬空指针;赋值运算符需检查自赋值、释放旧资源、深拷贝新资源;析构函数不可遗漏;优先使用std::unique_ptr等RAII容器替代裸指针。

拷贝构造函数必须深拷贝指针成员
当类里有 int*、std::string* 或其他动态分配资源(如 new 出来的数组)时,编译器默认的拷贝构造函数只做浅拷贝——两个对象的指针变量指向同一块内存。一旦其中一个析构时 delete 了它,另一个再访问就会崩溃或未定义行为。
正确做法是:在拷贝构造函数里为每个指针成员重新 new 内存,并逐字节或按逻辑复制内容。例如:
class Data {
int* ptr;
public:
Data(int val) : ptr(new int(val)) {}
Data(const Data& other) : ptr(new int(*other.ptr)) {} // 深拷贝
~Data() { delete ptr; }
};
注意:new int(*other.ptr) 是关键——不是 ptr = other.ptr,也不是 ptr = new int 后忘了赋值。
重载 operator= 要处理自赋值和旧资源释放
赋值运算符重载比拷贝构造更复杂,因为要先清理当前对象已有的资源,再复制新内容。漏掉自赋值检查(this == &other)会导致 delete ptr; ptr = nullptr; 后又去读 *other.ptr,直接崩。
立即学习“C++免费学习笔记(深入)”;
标准写法应包含三步:
- 自赋值检查:
if (this == &other) return *this; - 释放已有资源:
delete ptr; - 深拷贝新资源:
ptr = new int(*other.ptr);
更安全的做法是用“拷贝-交换”惯用法(copy-and-swap),但对简单类来说,显式三步更直观、易调试。
不写析构函数就别妄想深拷贝能工作
深拷贝和析构是绑定动作:你用 new 分配,就必须用 delete 释放;否则即使拷贝逻辑全对,也会内存泄漏。C++ 不会自动跟踪哪些指针是你 new 出来的。
常见疏忽:
- 只写了拷贝构造和
operator=,却忘了写析构函数 - 析构里漏掉了某个指针成员的
delete - 用了
std::vector却还手动管理其内部指针(没必要,它自己深拷贝)
原则:只要类里出现裸指针 + new,三个函数(拷贝构造、赋值、析构)必须成套实现,缺一不可。
现代 C++ 更推荐用 RAII 容器替代裸指针
手写深拷贝容易出错,尤其嵌套多层或异常路径下。除非练手或对接 C 接口,否则优先用 std::unique_ptr、std::vector、std::string 等自带深拷贝语义的类型。
例如:
class SafeData {
std::unique_ptr ptr;
public:
SafeData(int val) : ptr(std::make_unique(val)) {}
// 无需写拷贝构造、operator=、析构 —— 编译器生成的就对
};
这时编译器默认生成的拷贝构造会调用 std::unique_ptr 的拷贝(实际是移动,因为 unique_ptr 不可拷贝;若需拷贝,改用 std::shared_ptr)。真正需要你操心的,只是「要不要拷贝」这个语义问题,而不是「怎么安全分配/释放」这个实现问题。
裸指针深拷贝不是技术难点,而是维护负担——稍不留神,new 和 delete 就会错位,而这种错误往往在运行很久后才暴露。









