浅拷贝是C++默认行为,仅复制指针地址而非内容,导致双删、野指针等问题;含裸指针或动态资源的类必须手动实现深拷贝:分配新内存→复制数据→更新指针。
浅拷贝就是默认的,但多数时候它会出问题
当你没写拷贝构造函数时,C++ 会自动生成一个——它逐字节复制对象内存,对指针成员只复制地址,不复制指向的内容。这意味着两个对象的指针变量指向同一块堆内存。一旦其中一个析构时
了这块内存,另一个再访问就是野指针;若都试图
,直接触发
错误。
常见错误现象:
、
、程序在析构阶段崩溃,且只在对象被拷贝(如传值返回、容器插入)后才出现。
只要类里有裸指针(
、
等)、或管理了动态资源(文件句柄、socket、GPU buffer),就必须自己写拷贝构造函数
、
这类标准容器内部已实现深拷贝,它们的成员不用额外处理
编译器不会警告你“你该写深拷贝”,它只安静地给你一个危险的默认行为
深拷贝必须手动分配新内存并复制内容
核心动作就三步:申请新内存 → 复制原数据 → 更新指针成员。别漏掉任何一步,尤其不能只
不
或赋值。
假设有个类管理一个动态数组:
立即学习
“
C++免费学习笔记(深入)
”;
如果用
,对
没问题;但对含虚函数、非 POD 类型(比如含
的结构体),必须用循环或
别在拷贝构造函数里调用
—— 它依赖已构造完成的对象,而此时当前对象还没构造完
记得同步更新所有相关成员:比如
、
、引用计数等,漏掉一个就可能造成越界或逻辑错乱
拷贝构造函数和 operator= 必须一起改,否则行为不一致
只改拷贝构造函数,不改赋值运算符,会导致“同一个类,两种拷贝逻辑”:初始化时深拷,赋值时却浅拷——这是最隐蔽的坑之一。
C知道
CSDN推出的一款AI技术问答工具
下载
典型错误场景:把对象先声明,再用
赋值;或者 vector 扩容时触发移动/拷贝混合操作。
赋值运算符要先检查自赋值:
,否则
后再读
就崩了
赋值时要先释放旧资源,再分配新资源,顺序反了会内存泄漏
C++11 后推荐用“拷贝-交换”惯用法(copy-and-swap),能自动处理自赋值和异常安全,但要注意临时对象开销
现代 C++ 更推荐用 RAII 和智能指针绕过手动深拷贝
如果你发现自己反复在写深拷贝逻辑,大概率是设计上可以优化:把资源管理从裸指针移到
或
,很多问题就消失了。
例如:
的拷贝构造默认是删除的(禁用),所以你仍需手动实现——但它帮你管好了
,不会忘
拷贝是共享引用计数,本质是浅拷贝语义,但行为安全;适合读多写少、需共享所有权的场景
RAII 不是银弹:如果业务逻辑确实要求物理隔离(比如两个 Buffer 必须互不影响),那深拷贝仍是唯一选择,只是实现更可靠了
真正容易被忽略的是:深拷贝成本本身。一次拷贝几百 MB 的图像数据?要考虑是否真需要——有时传
或用 move 语义更合理。
deletedeletedouble freedouble free or corruptionsegmentation faultint*char*std::stringstd::vectornewmemcpy
class Buffer {
int* data_;
size_t size_;
public:
Buffer(const Buffer& other) : size_(other.size_) {
data_ = new int[size_]; // ① 分配新内存
for (size_t i = 0; i < size_; ++i) {
data_[i] = other.data_[i]; // ② 逐元素复制(不是 memcpy!除非确定 POD)
}
}
};
memcpy(data_, other.data_, size_ * sizeof(int))intstd::stringstd::copyoperator=size_capacity_=if (this == &other) return *this;delete data_other.data_std::unique_ptrstd::shared_ptr
class Buffer {
std::unique_ptr data_;
size_t size_;
public:
Buffer(const Buffer& other) : size_(other.size_),
data_(std::make_unique(other.size_)) {
std::copy(other.data_.get(), other.data_.get() + size_, data_.get());
}
};
std::unique_ptrdeletestd::shared_ptrconst Buffer&