拷贝构造函数必须深拷贝:为指针成员new新内存并复制内容,避免double free;operator=需先释放旧资源、防自赋值;析构函数仅delete自身内存;推荐用std::string/std::vector等自动管理资源。

拷贝构造函数里必须 new 一份新内存
默认拷贝构造函数只复制指针值,ptr 成员被原样赋给新对象,结果两个对象的 ptr 指向同一块堆内存。析构时各自调用 delete ptr,触发 double free 错误。
正确做法是在拷贝构造函数中为指针成员分配新内存,并把原对象的数据复制进去:
MyString(const MyString& other) {
len = other.len;
ptr = new char[len + 1];
strcpy(ptr, other.ptr); // 复制内容,不是地址
}
- 不能直接写
ptr = other.ptr—— 这是浅拷贝的根源 - 必须检查
other.ptr是否为nullptr,避免strcpy(nullptr)崩溃 - 如果类中有多个指针成员(比如
int* data和char* name),每个都要单独new+ 复制
operator= 必须先释放旧资源再拷贝
赋值和构造不同:对象已经存在,ptr 可能已指向有效内存。不先 delete 就直接 new,会造成内存泄漏;不检查自赋值(a = a)则可能 delete ptr 后又去 strcpy(ptr, ...),访问已释放内存。
标准写法要包含三步:清理旧资源 → 分配并拷贝新资源 → 返回 *this:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
MyString& operator=(const MyString& other) {
if (this == &other) return *this; // 自赋值保护
delete[] ptr; // 先释放当前持有的内存
len = other.len;
ptr = new char[len + 1];
strcpy(ptr, other.ptr);
return *this;
}
- 顺序不能颠倒:先
delete再new,否则异常抛出时旧资源没释放,新资源又没拿到,就泄漏了 - 返回
MyString&类型且是return *this,否则a = b = c会编译失败
析构函数里只管 delete 自己的 ptr,别越界
只要拷贝构造和 operator= 做对了,析构函数就很简单:只负责释放本对象在构造或赋值时 new 出来的那块内存。
~MyString() {
delete[] ptr; // 不需要判空,delete nullptr 是安全的
}
- 不要在析构里写
if (ptr) { delete[] ptr; ptr = nullptr; }——ptr = nullptr没意义,对象马上销毁 - 更不要试图去“判断这块内存是不是自己分配的”——C++ 没这种机制,靠的是你全程严格遵守 Rule of Five
用智能指针或容器替代裸指针最省心
如果你控制不了类的设计节奏,或者团队里有人容易漏写 operator=,直接换掉裸指针是最稳妥的防错方式。
- 用
std::string替代char*:它内部已实现深拷贝,MyClass{ std::string name; }完全不用写任何拷贝函数 - 用
std::vector<int></int>替代int*:同样自带深拷贝语义 - 必须用指针语义?选
std::shared_ptr<t></t>而不是std::unique_ptr<t></t>—— 前者支持拷贝,后者默认禁止,强行拷贝会编译报错use of deleted function
Rule of Zero 的核心不是“不写代码”,而是“让资源管理责任下沉到已验证过的组件里”。裸指针深拷贝的坑,不在语法难,而在漏掉某一个环节就会崩溃——而那个环节,往往出现在改代码半年后的某次重构里。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










