第1章:指针与引用的基础辨析
一、导读
1.1 本章学习目标
第1章辨析 C++ 中最基础也最容易混淆的两个概念:指针和引用。Meyers 在此给出了何时使用指针、何时使用引用的清晰指导。
本章核心学习目标:
- 理解指针和引用的本质区别:空值、重新绑定、语法差异
- 掌握选择策略:什么时候用指针?什么时候用引用?
- 理解类型安全和空值处理
1.2 为什么本章如此重要
指针和引用的选择影响代码的安全性、可读性和性能。错误的选择会导致空指针解引用、内存泄漏等问题。
1.3 核心问题引导
引用和指针在底层实现上有什么区别?
为什么函数参数优先使用 const 引用?
什么时候必须使用指针?
二、核心概念详解
2.1 指针 vs 引用的区别
cpp
// 引用: 必须是有效对象的别名,不能为空,不能重新绑定
int x = 42;
int& ref = x; // ref 是 x 的别名
// int& null_ref; // 错误: 引用必须初始化
// ref = y; // 这是赋值,不是重新绑定
// 指针: 可以为空,可以重新指向
int* ptr = &x; // ptr 指向 x
ptr = nullptr; // OK: 指针可以为空
int y = 100;
ptr = &y; // OK: 指针可以重新指向
// 语法差异:
*ptr = 10; // 需要解引用
ref = 10; // 直接使用
// 底层实现:
// 引用通常用指针常量实现: int* const ref = &x;
// 但编译器可以做更多优化(如直接替换为原变量)2.2 选择策略
cpp
// 使用引用的场景:
// 1. 函数参数(避免拷贝,表达"不为空"的语义)
void process(const std::string& name); // 不修改,不拷贝
void modify(std::vector<int>& data); // 需要修改
// 2. 操作符重载
std::ostream& operator<<(std::ostream& os, const Widget& w);
// 3. 返回左值
int& getFirstElement(std::vector<int>& vec) {
return vec[0]; // 返回引用,允许修改
}
// 使用指针的场景:
// 1. 可能为空
Widget* findWidget(int id) {
// 可能找不到,返回 nullptr
}
// 2. 需要重新指向
Widget* current = nullptr;
for (auto& w : widgets) {
if (w.isActive()) {
current = &w; // 指向不同的 widget
break;
}
}
// 3. 数组操作
void processArray(int* arr, size_t size) {
for (size_t i = 0; i < size; ++i)
arr[i] *= 2;
}2.3 避免返回指向局部变量的引用
cpp
// 错误: 返回局部变量的引用
const std::string& getGreeting() {
std::string greeting = "Hello";
return greeting; // greeting 在函数结束时销毁!
}
// 正确: 返回值(利用移动语义)
std::string getGreetingSafe() {
std::string greeting = "Hello";
return greeting; // 移动构造或 NRVO
}
// 正确: 返回成员变量的引用
class Config {
std::string name;
public:
const std::string& getName() const { return name; } // OK: 成员变量生命周期足够长
};三、实战案例
3.1 案例一:引用和指针的性能差异
cpp
#include <benchmark/benchmark.h>
// 引用版本: 编译器更容易优化
void addRef(const int& a, const int& b, int& result) {
result = a + b;
}
// 指针版本: 编译器需要处理别名问题
void addPtr(const int* a, const int* b, int* result) {
*result = *a + *b;
}
// 引用版本通常生成更高效的代码
// 因为编译器知道引用不会为空,不会有别名问题四、本章小结
引用不能为空、不能重新绑定,适合表达"必不为空"的语义
指针可以为空、可以重新指向,适合可选值和数组操作
函数参数优先使用 const 引用,避免不必要的拷贝
五、思考与练习
思考题
为什么 C++ 需要引用?指针不够用吗?
右值引用(&&)和左值引用(&)有什么区别?
编程练习
实现一个可选类型(Optional),内部使用指针但对外提供引用接口。
重写一个使用指针的函数,改为使用引用。