拷贝构造函数参数必须用引用传递,否则会因值传递触发无限递归导致编译错误或栈溢出;引用避免构造新对象,const 引用还可接受临时对象;赋值运算符虽可值传参但效率低,且浅拷贝易引发资源管理问题。

因为值传递会触发无限递归调用,编译器直接报错或栈溢出——这不是风格建议,而是语言规则强制要求。
拷贝构造函数参数用值传递会怎样
只要写成 MyClass(MyClass obj),编译器在尝试调用它时就会陷入死循环:
- 调用
MyClass a(b)时,要把b传给形参obj - 而传值意味着要构造
obj,这又得调用拷贝构造函数 - 于是再次进入
MyClass(MyClass obj),再试图构造新的obj…… - 没有出口,直到栈空间耗尽或编译器在编译期拒绝(GCC/Clang 通常直接报错)
错误信息类似:error: call to implicitly-deleted copy constructor of 'MyClass' 或更直白的 infinite recursion evaluating default argument(取决于上下文)。
为什么引用就能破局
引用不构造新对象,只是给原对象起个别名,绕过了“为传参而调用拷贝构造”这一环:
-
MyClass(const MyClass& other)中的other和实参是同一块内存 - 没有新对象诞生,也就无需调用任何构造函数
- 加
const是惯例:既防止误改源对象,也允许传入临时对象(如func(MyClass()))
注意:MyClass(MyClass* ptr) 是合法的普通构造函数,但不是拷贝构造函数——编译器不会在需要拷贝语义时自动选它。
哪些场景会隐式触发拷贝构造
不是只有 MyClass a = b; 才调用。以下写法全都会走拷贝构造(前提是没被 RVO/NRVO 优化掉):
- 函数值传参:
void foo(MyClass x) { ... }; foo(obj); - 函数值返回:
MyClass bar() { return obj; }; auto x = bar(); - 初始化式写法:
MyClass a(obj);和MyClass b = obj;(后者是语法糖,等价于前者)
这些地方如果类没定义拷贝构造,且编译器生成的默认版本可用,就用默认的;否则必须自己写——而且参数类型只能是引用。
赋值运算符重载可以不用引用吗
可以,但不推荐:MyClass& operator=(MyClass other) 合法,但会多一次无谓的拷贝(先拷贝进形参,再赋值)。而 MyClass& operator=(const MyClass& other) 避免了这次拷贝,也符合常规用法。它不会递归,因为赋值操作本身不依赖拷贝构造来传参。
真正容易被忽略的是:即使你没写拷贝构造,只要类里有指针成员或自定义资源管理逻辑,编译器自动生成的浅拷贝大概率引发 double-free 或悬垂指针——这时候引用参数只是起点,深拷贝实现才是关键。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











