引用使用场景
struct 的实例按值复制,函数之间传递数据时同样面临选择:复制一份,还是让函数直接使用调用者的对象。本章给出按场景选型的参数表,先说明引用适合的场景。可空与所有权的场景由下一章的指针接手。
传递方式
下表来自 Google C++ Style Guide 的 Inputs and Outputs 一节,是这份笔记与姊妹仓库工程代码共同的传参依据:
| 场景 | 推荐方式 | 说明 |
|---|---|---|
只读小对象(int、double、小 struct) |
按值 | 拷贝廉价,调用处清晰 |
只读大对象(string、vector、大结构) |
const T& |
避免昂贵拷贝 |
| 需要修改且必须存在 | 非 const 引用 T& |
非可选输出用引用,不可为空 |
| 需要修改且可能为空 | T* |
可选输出用指针 |
| 输入输出且必须存在 | T& |
同非可选输出 |
| 输入输出且可能为空 | T* |
可选输入输出 |
| “本该是引用”的可选只读输入 | const T* |
可空输入 |
| 转移所有权 | std::unique_ptr<T> 按值 |
所有权随参数转移 |
| 共享所有权 | std::shared_ptr<T> |
按值或 const&,视引用计数而定 |
许多旧文与 cpplint 时代的检查建议输出参数一律用指针。现行指南改为:不可为空的输出用非 const 引用,只有“可选”才交给指针。选型先问三件事:只读还是修改,对象大不大,可不可以为空。
只读与必改
完整示例覆盖表中的前三类参数场景,一个函数对应一种传递方式:
reference-usage-scenarios.cpp
// 引用使用场景示例:按场景为参数选择按值、const 引用与非 const 引用。
#include <iostream>
#include <string>
#include <vector>
// 只读小对象:按值传递,拷贝廉价,调用处清晰。
int SumCoords(int row, int col) {
return (row * 10) + col;
}
// 只读大对象:const 引用避免拷贝,函数承诺不修改元素。
int TotalChars(const std::vector<std::string>& words) {
int total = 0;
for (const std::string& word : words) {
total += static_cast<int>(word.size());
}
return total;
}
// 需要修改且必须存在:非 const 引用承担输出,不可为空。
void Capitalize(std::string& text) {
for (char& ch : text) {
if (ch >= 'a' && ch <= 'z') {
ch = static_cast<char>(ch - 'a' + 'A');
}
}
}
int main() {
std::vector<std::string> words{"board", "game", "grid"};
std::string title{"tic tac toe"};
std::cout << "坐标和: " << SumCoords(1, 2) << "\n";
std::cout << "总字符数: " << TotalChars(words) << "\n";
Capitalize(title);
std::cout << "大写标题: " << title << "\n";
return 0;
}运行结果:
运行效果
坐标和: 12
总字符数: 13
大写标题: TIC TAC TOE
调用 Capitalize(title) 后,调用者手里的 title 已经变成大写,修改直接落到了原对象上。SumCoords 与 TotalChars 的结果都是单个值,函数按值返回。
与返回值的取舍:结果只是一个值时优先返回,SumCoords 与 TotalChars 都是例子。需要就地修改调用者已有的对象,或一次产生多个结果时,才用非 const 引用承担输出。
引用必须绑定到一个对象,没有“空引用”。Capitalize 因此不需要判空检查。“可能没有”的场景留给下一章的指针。
常见错误
必改参数按值传
函数内修改的只是副本,对象保持原样,要让修改生效,把参数改成非 const 引用,并确认调用处传入的是可修改的对象。
返回局部引用
局部变量在函数结束时销毁,返回它的引用会得到悬空引用,计算结果优先按值返回,让编译器处理拷贝与移动。
只读参数全加引用
int、double 这类小对象按值传递更清晰,const T& 只为较大的对象省拷贝,照表选型,不把单一写法套到所有参数上。
自测问题
TotalChars的参数为什么是const std::vector<std::string>&?查看答案
它只读取元素不修改,参数带
const让编译器替读者守住这条承诺。vector的拷贝昂贵,按引用免去整表复制。Capitalize为什么不返回新字符串?查看答案
它的任务是改写调用者已有的对象,非
const引用让修改直接生效。只产生一个新结果时才优先返回值。调用
Capitalize前需要判空吗?查看答案
不需要。引用必须绑定到对象,不存在空引用。“可能没有”的场景属于指针,见下一章。
本章回顾
你现在能按场景表选择按值、const T& 与 T&。完整示例见 reference-usage-scenarios.cpp,由分册根 CMakeLists.txt 收集编译;可空输入、可选输出与所有权见下一章指针使用场景。