指针使用场景
上一章的引用不可为空,参数必须绑定到一个对象。但“有没有这项信息”常常由调用者决定,参数需要能表达“没有”。本章沿同一张场景表展开指针的场景,并给出所有权选择的入口。
指针的场景
| 场景 | 推荐方式 | 说明 |
|---|---|---|
| “本该是引用”的可选只读输入 | const T* |
传 nullptr 表示没有 |
| 需要修改且可能为空(可选输出) | 非 const 指针 T* |
调用处可见 & |
| 可选按值输入 | std::optional<T> |
现行指南的首选写法 |
| 转移所有权 | std::unique_ptr<T> 按值 |
所有权随参数转移 |
| 共享所有权 | std::shared_ptr<T> |
按值或 const&,视引用计数而定 |
指针与引用的分界只有一条:参数可不可以为空。必须存在就用引用,可能没有才用指针。调用处由此一眼看出哪些参数可以不提供。
可空与可选
完整示例覆盖表中前两个场景:
pointer-usage-scenarios.cpp
// 指针使用场景示例:可空输入、可选输出与判空约定。
#include <iostream>
#include <string>
// 可选的只读输入:const 指针,传 nullptr 表示没有这项信息。
void PrintTask(const std::string& title, const std::string* note) {
std::cout << "任务: " << title << "\n";
if (note != nullptr) {
std::cout << "备注: " << *note << "\n";
}
}
// 可选输出:非 const 指针;找到首个数字时写入并返回 true。
bool TryFirstDigit(const std::string& text, int* digit_out) {
for (char ch : text) {
if (ch >= '0' && ch <= '9') {
*digit_out = ch - '0';
return true;
}
}
return false;
}
int main() {
const std::string note{"每周重开一局"};
PrintTask("井字棋", ¬e);
PrintTask("自由练习", nullptr);
int digit = 0;
if (TryFirstDigit("第 3 局", &digit)) {
std::cout << "首个数字: " << digit << "\n";
}
if (!TryFirstDigit("残局", &digit)) {
std::cout << "残局不含数字\n";
}
return 0;
}运行结果:
运行效果
任务: 井字棋
备注: 每周重开一局
任务: 自由练习
首个数字: 3
残局不含数字
PrintTask 在解引用前先判空。TryFirstDigit 找不到数字也属正常结果,不是错误。调用处写 &digit,取地址的 & 让“这个参数会被写入”在调用行可见。
堆对象的所有权要移交时,用 std::unique_ptr<T> 按值传参。多处共享同一对象时,用 std::shared_ptr<T>。两者的语法与生命周期管理见内存分册。
常见错误
解引用前不判空
指针参数的约定是“可能为空”,解引用前先用 p != nullptr 确认,跳过判空时程序在空指针上直接崩溃。
指针传必存参数
调用者可能误传 nullptr,函数内被迫防御判空,接口约定反而模糊,必须存在的参数按上一章的表用 T& 或 const T&。
对象传给指针
调用 PrintTask(title, note) 少写一个 &,编译器直接报类型错误,指针参数在调用处写 &对象,取地址本身就是接口约定的一部分。
自测问题
PrintTask的note参数为什么用const std::string*而不是const std::string&?查看答案
备注是可选信息,调用者要能表达“没有”。指针允许传
nullptr,引用必须绑定到对象。TryFirstDigit为什么返回bool还要一个int*参数?查看答案
找不到首个数字是正常结果,
bool报告成败,int*只在成功时写入。这就是可选输出的场景。转移堆对象的所有权应该用什么参数?
查看答案
std::unique_ptr<T>按值传参,所有权随参数移交。多处共享才用std::shared_ptr<T>。
本章回顾
你现在能用指针表达可空输入与可选输出,并记住先判空再解引用。完整示例见 pointer-usage-scenarios.cpp,由分册目录下的 CMakeLists.txt 收集编译;所有权与生命周期见内存分册。