值传递会触发拷贝构造,应避免用于大对象;推荐使用const MyClass&实现零开销且安全的引用传递,仅在需修改原对象时用MyClass&,指针适用于可选语义场景。

值传递会触发拷贝构造,别轻易用在大对象上
把 MyClass 对象直接当参数写进函数括号里,比如 void func(MyClass obj),C++ 就会调用拷贝构造函数创建一个副本。这个副本只在函数内有效,函数返回后就销毁,任何修改都不影响原始对象。
问题在于:如果类里有 std::vector、std::string 或者自己管理的堆内存(比如 int* 成员),拷贝开销可能很大;更糟的是,若没显式定义拷贝构造函数,编译器生成的默认版本可能做浅拷贝,导致两个对象共享同一块堆内存,析构时重复释放——double free 错误就来了。
- 仅适用于小型、无资源管理的类(如纯数据结构
struct Point { int x, y; };) - 想避免拷贝但又不打算修改原对象?改用
const MyClass& - C++11 后可配合
std::move强制移动语义:func(std::move(obj)),但之后obj进入有效但未定义状态
引用传递是默认首选,加 const 更安全
写成 void func(const MyClass& obj),既不拷贝对象,又禁止函数内部修改它。底层只是传地址,零额外开销,还能绑定到临时对象(比如 func(MyClass(42)) 也合法)。
注意:不加 const 的引用(MyClass&)只能绑定到左值(具名变量),不能传字面量或函数返回的临时对象,否则编译报错:cannot bind non-const lvalue reference to an rvalue。
立即学习“C++免费学习笔记(深入)”;
- 读取对象内容(打印、计算、比较)一律用
const MyClass& - 需要修改原始对象时才用
MyClass&,比如void reset(MyClass& obj) - 模板函数中常用
const T&适配任意类型,兼顾效率与通用性
指针传递要手动解引用,容易漏判空指针
声明为 void func(MyClass* ptr),调用时必须传地址:func(&obj)。函数内所有访问都要用 ->,且第一步几乎总得检查是否为空:if (ptr == nullptr) return;。
相比引用,指针多一层间接性,也多了空悬风险。但它能表达“可选”语义(nullptr 表示无对象),而引用必须绑定有效对象。
- 当参数确实可能为空(比如可选配置对象),用指针比引用更明确
- 避免裸指针跨函数传递;优先考虑
std::optional<myclass></myclass>(C++17)或智能指针 - 不要返回局部对象的指针,也不要让指针生命周期超出所指对象
模板函数接收类对象时,类型推导规则很关键
写模板如 template<typename t> void process(T obj)</typename>,传入 MyClass 实例时,T 被推导为 MyClass(值传递),照样触发拷贝。想让它自动适配引用?得写成 template<typename t> void process(const T& obj)</typename>,这时 T 推导为 MyClass,整个形参类型就是 const MyClass&。
更进一步,若想完美转发(保留实参是左值/右值的性质),要用万能引用:template<typename t> void process(T&& obj)</typename>,再配合 std::forward<t>(obj)</t>。但这属于进阶场景,日常读取操作没必要上。
- 普通模板函数参数,优先写
const T&,而不是裸T - 类模板实例化后的对象传参,比如
Person<string int></string>,函数签名里可以直接写死类型,也可以用模板参数泛化 -
typeid(T).name()可用于调试推导结果,但输出格式依赖编译器,不可用于逻辑判断
实际写代码时,最常踩的坑不是语法错误,而是混淆了“要不要改原对象”和“要不要避免拷贝”这两件事。前者决定用不用 const,后者决定用不用引用;把这两个维度拆开想,选择就清晰了。


















