浅拷贝是位复制导致裸指针共享同一内存,引发double free或use-after-free;深拷贝需手动分配并复制资源,遵循三/五法则;推荐用raii和智能指针替代手写深拷贝。

浅拷贝就是指针一模一样地复制
当你没写 copy constructor 或 operator=,C++ 默认做的就是位拷贝:把对象里每个字节原样复制过去。如果类里有 int* 这种裸指针,那两个对象的指针变量会指向同一块堆内存。
常见错误现象:double free 或 use-after-free —— 一个对象析构时 delete 了指针,另一个再访问就崩;或者两个对象先后析构,第二次 delete 同一块地址直接触发未定义行为。
使用场景:纯数据结构(比如只含 int、std::string、std::vector 的类)通常不需要手动干预,因为这些类型自己管深拷贝;但只要出现裸指针、文件描述符、socket 句柄等资源句柄,就必须警惕。
深拷贝要为每个对象单独分配并复制资源
核心动作是:在 copy constructor 和 operator= 里,对每个动态资源做 new + memcpy 或逐字段赋值,而不是直接复制指针值。
立即学习“C++免费学习笔记(深入)”;
实操建议:
- 必须同时实现
copy constructor、operator=和destructor(三法则/五法则) -
operator=要先检查自赋值:if (this == &other) return *this; - 分配新内存前,记得释放当前对象已持有的资源(避免内存泄漏)
- 考虑用
std::unique_ptr或std::shared_ptr替代裸指针——它们自动处理深拷贝语义(unique_ptr禁止拷贝,shared_ptr拷贝是增加引用计数)
示例片段:
class Buffer {
char* data_;
size_t size_;
public:
Buffer(const char* s) : size_(strlen(s)) {
data_ = new char[size_ + 1];
strcpy(data_, s);
}
Buffer(const Buffer& other) : size_(other.size_) { // copy constructor
data_ = new char[size_ + 1]; // 关键:新分配
strcpy(data_, other.data_);
}
Buffer& operator=(const Buffer& other) {
if (this == &other) return *this;
delete[] data_; // 先清理
size_ = other.size_;
data_ = new char[size_ + 1]; // 再分配
strcpy(data_, other.data_);
return *this;
}
~Buffer() { delete[] data_; }
};
移动语义让“假深拷贝”变成真效率提升
很多场景下你并不真需要两份独立副本,只是想把资源从临时对象“拿走”。这时候用移动构造函数比深拷贝快得多,而且天然规避资源争用——因为源对象被置为有效但未定义状态(比如指针设为 nullptr),不会再析构原资源。
性能影响明显:深拷贝 std::vector<int>(1e6)</int> 是 O(n) 时间+内存;移动只是交换三个指针,O(1)。
容易踩的坑:
- 写了移动操作但忘了把旧指针置空,导致移动后源对象析构时二次
delete - 误以为
std::move(x)会真的移动——它只是类型转换,不执行任何操作;真正移动发生在移动构造或移动赋值里 - 继承体系中基类没声明
virtual移动函数,派生类对象被切片后无法正确移动
RAII 是比手写深拷贝更底层的解法
资源争用问题本质不是“拷贝方式错了”,而是“资源生命周期没绑定到对象生命周期”。RAII 把资源获取和释放封装进构造和析构,让编译器帮你确保不会漏掉清理。
为什么这样做:
- 避免手写拷贝逻辑出错——人容易忘写、写错顺序、漏异常安全处理
- 现代 C++ 标准库容器和智能指针都基于 RAII,直接组合使用就能获得正确拷贝语义
- 一旦用了
std::unique_ptr<t></t>,你就不用再管深/浅拷贝:它禁止拷贝、允许移动,且移动后源为空
真实项目中,95% 的深拷贝需求其实可以通过替换裸指针为 std::vector、std::string、std::unique_ptr 消解。手写深拷贝只在极少数场景必要:比如封装 C API 返回的 void*、自定义内存池管理、或性能敏感且必须控制布局的结构体。
最容易被忽略的一点:深拷贝的“深”是相对的。如果类里有个 std::shared_ptr<foo></foo>,拷贝它只是增加引用计数——这算浅还是深?答案是:语义上它是深拷贝(用户视角两个对象互不影响),但实现上是共享的。别被术语带偏,盯住资源所有权和生命周期就行。









