拷贝构造函数签名必须为ClassName(const ClassName&),且需public、非explicit、无默认参数;含裸指针或系统资源时必须自定义以实现深拷贝,否则默认浅拷贝将导致double free等未定义行为。

拷贝构造函数的签名必须是 ClassName(const ClassName&)
它不是随便叫个名字就行的构造函数,C++ 编译器只认这个特定签名。漏掉 const、用值传递(ClassName(ClassName))、加 volatile、多一个参数,都会导致编译器不把它当拷贝构造函数用——此时可能调用默认生成的,也可能报错,取决于上下文。
常见错误现象:error: use of deleted function 'A::A(A)' 或静默调用默认版本,结果浅拷贝出问题。
- 必须是
public访问级别(否则对象无法被拷贝) - 不能是
explicit(否则禁止隐式拷贝,比如函数传参、返回值会失败) - 不能有默认参数(否则会被视为普通构造函数重载,破坏唯一性)
什么时候必须自己写?——有指针或资源管理时
默认拷贝构造函数只做成员逐个复制(bitwise copy),对 std::string、std::vector 这类类型没问题,但一旦类里有裸指针、文件描述符、动态分配内存,就大概率出问题。
典型场景:类中含 char* 成员指向堆内存;或封装了 FILE*、int fd 等需要独占的系统资源。
立即学习“C++免费学习笔记(深入)”;
- 不写 → 默认拷贝后两个对象指向同一块内存 → 一个析构释放,另一个再析构就
double free - 写了 → 可在内部用
new分配新内存 +strcpy复制内容,实现深拷贝 - 现代 C++ 更推荐用
std::unique_ptr替代裸指针,它自带正确的拷贝/移动语义
std::vector 里存自定义类,会触发拷贝构造吗?
会,但只在扩容或插入中间位置时发生。只要没触发 reallocation,push_back 用的是移动(如果类有移动构造)或拷贝构造(否则)。
性能影响明显:若拷贝构造里做了大量内存分配或 IO,vector 扩容会变慢,甚至引发意外异常。
- 测试是否被调用:在拷贝构造函数里加
std::cout - 想避免?确保类可移动(定义移动构造 + 移动赋值),并用
std::move(obj)传入 - 或者提前
reserve足够空间,减少拷贝次数
容易被忽略的隐式调用点
很多人只想到 A a = b; 这种显式写法,其实下面这些地方也会悄悄调用拷贝构造:
- 函数以值传递接收参数:
void f(A x) { ... }→ 调用A(const A&)构造形参x - 函数返回局部对象:
A g() { A tmp; return tmp; }→ C++17 前可能调用(即使启用 RVO) - 抛出异常对象:
throw A{};→ 若异常对象需复制到栈外存储区
这些地方一旦拷贝构造出错(比如抛异常、内存不足),程序直接终止,很难定位。
真正麻烦的不是怎么写,而是它总在你没注意的地方被调用,而且一出问题就是崩溃或未定义行为。


















