默认拷贝构造对内置类型成员只做值复制,对自定义类型成员递归调用其拷贝构造;若类管理动态资源(如裸指针、文件句柄),则必须手动实现深拷贝或禁用拷贝。

默认拷贝构造对内置类型成员只做值复制
编译器生成的默认拷贝构造函数,对 int、double、char、bool、enum 以及原生指针(如 int*)这类内置类型成员,直接按内存字节逐字拷贝——也就是“值拷贝”。它不关心语义,也不调用任何构造逻辑。
这意味着:
- 如果原对象的
int x = 42,新对象的x也是42,两者完全独立 - 如果原对象有
char* p = new char[10],默认拷贝后,新对象的p指向**同一块内存地址**,不是新开辟空间 - 这种指针复制就是典型的浅拷贝,后续析构时容易 double free
默认拷贝构造对自定义类型成员会递归调用其拷贝构造
当类中某个成员是另一个类(比如 std::string、std::vector 或你自己写的 Time 类),且该成员类型自己定义了拷贝构造函数,那么默认拷贝构造会自动调用它的拷贝构造——而不是简单复制指针或字节。
例如:
立即学习“C++免费学习笔记(深入)”;
class Person {
std::string name; // 自定义类型,有自己拷贝构造
Time birth; // 假设 Time 也实现了拷贝构造
int id; // 内置类型,直接值拷贝
};当你写 Person p2(p1),编译器默认行为是:
- 调用
std::string的拷贝构造,深拷贝字符串内容 - 调用
Time的拷贝构造(如果你写了),否则调用其默认拷贝构造 - 对
id直接赋值p1.id
没有显式定义拷贝构造时,编译器不会帮你处理资源所有权
这是最容易出问题的地方。只要类里出现动态资源(比如 new 出来的内存、打开的文件描述符、持有的 mutex 等),默认拷贝构造就大概率埋雷。
典型错误模式:
- 两个对象共享同一块堆内存,其中一个析构时
delete了,另一个再析构就 crash - 拷贝后两个对象都试图 close 同一个 fd,第二个 close 返回 -1 并可能干扰 errno
- 多线程下两个对象同时操作同一份内部缓冲区,引发数据竞争
解决方式不是“禁用拷贝”,而是明确选择:= default(确认浅拷贝安全)、= delete(禁止拷贝),或手写深拷贝逻辑。
判断要不要自己写拷贝构造,看类是否管理资源
一句话标准:类里有没有“拥有”某种外部资源? 如果有,就别依赖默认拷贝构造。
常见需手动干预的场景:
- 成员含裸指针(
int* data)且指向堆内存 - 成员含
FILE*、SOCKET、pthread_mutex_t等系统句柄 - 类内部缓存了全局状态(如单例引用、静态计数器)且拷贝后不该共享
- 使用了
std::unique_ptr—— 它本身已禁用拷贝,此时默认拷贝构造会编译失败,必须显式处理
真正安全的默认拷贝,只存在于“纯数据聚合”类中:所有成员都是值语义类型(int、std::string、std::array 等),且不涉及跨对象共享状态。


















