拷贝构造函数参数必须是const t&,否则会导致无限递归;因值传递需调用自身构造函数,引用传递则避免构造且支持临时对象绑定,const确保只读语义与标准库兼容。

拷贝构造函数参数不能是值传递,否则会无限递归
这是最根本的限制:如果写成 ClassName(ClassName other),编译器在把实参传入时,必须先构造形参 other——而这又需要调用 ClassName 的拷贝构造函数,形成死循环。栈会迅速耗尽,编译可能通过,但运行时大概率崩溃。
常见错误现象:Segmentation fault (core dumped) 或编译器警告 “call to implicitly-deleted copy constructor”,尤其在调试模式下容易暴露。
- 引用传递(
&)绕过构造过程,直接绑定到实参对象,不触发拷贝 - 指针也不行:
ClassName(const ClassName* p)是合法语法,但它不是拷贝构造函数——C++标准只认第一个参数为同类型引用的构造函数为拷贝构造函数 - 即使类里没显式定义拷贝构造函数,编译器生成的默认版本也严格遵循
const ClassName&签名
为什么加 const?不只是“防止修改”,更是接口契约
const 修饰不是可选项,而是语义必需。它让拷贝行为不可逆地表达“只读源对象”这一意图。
去掉 const(如 ClassName(ClassName& other))会导致两个问题:
- 无法绑定临时对象:比如
func(MyClass())或return MyClass();中的返回值,都是右值,只能绑定到const T&,不能绑定到非 const 引用 - 调用方失去安全预期:如果函数签名允许修改
other,使用者就不得不检查实现细节,才能确认“我传进去的对象会不会被悄悄改掉” - 多数标准库容器和算法(如
std::vector::push_back内部拷贝)都依赖 const 引用语义,松动它会破坏兼容性
内置类型和小对象也适用 const 引用吗?
语法上可以,但没必要。对 int、double、std::pair<int int></int> 这类 trivial 类型,值传递和 const 引用性能几乎无差别,且值传递更符合直觉。
真正受益的是那些拷贝开销大的类型:
-
std::string(尤其长字符串,涉及堆内存分配与复制) -
std::vector、std::map等容器(深拷贝所有元素) - 自定义类含裸指针或资源句柄(若未正确定义深拷贝,值传递可能引发 double-free)
注意:编译器的 RVO/NRVO 优化只作用于函数返回值,对参数传递无效——别指望它帮你“救”值传递的拷贝。
const 引用 vs 普通引用 vs 指针:选哪个?
拷贝构造函数只接受 const T&,这是语言硬性规定。但理解差异能避免误用:
-
const T&:安全、简洁、支持临时对象、无空悬风险,是唯一正确选择 -
T&(非 const):无法接收 const 对象或字面量,调用受限;且违背拷贝“不修改源”的本意 -
const T*:需手动解引用、有空指针风险、调用方得写func(&x),语法冗余;更重要的是——它不被视为拷贝构造函数,不会在初始化、值传参等场景自动调用
真正容易被忽略的点是:const 引用不仅防修改,还决定了你能不能把一个临时对象(比如函数返回的 std::string)直接用于拷贝初始化。漏掉 const,那一行看似正常的代码就会编译失败。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











