C++指针与引用:如何选择与使用? #
引言 #
在C++开发中,指针和引用是两个最基础也最容易混淆的概念。本文将通过实例深入分析它们的区别,并给出具体的使用建议。
指针和引用的区别 #
首先看一段代码:
int a = 1;
int b = 2;
int *c = nullptr; // 指针可以不初始化或初始化为nullptr
c = &a;
int &d = b; // 引用必须初始化
指针其实就是一个存放内存地址的整数,这个整数表示的是被指向的变量的地址。
引用其实就是变量的别名,就是给变量重新起了一个名字,注意引用既然是个别名,那它一定要有本体,一个人叫王二小,我们也可以给它起个别名叫二蛋,我们提到二蛋和王二小其实都是同一个人。
区别:
- 指针在声明时可以暂时不初始化,即
pointer = nullptr
,指针在生命周期内随时都可能是空指针,所以在每次使用时都要做检查,防止出现空指针异常问题,而引用却不需要做检查,因为引用永远都不会为空,它一定有本体,一定得代表某个对象,引用在创建的同时必须被初始化。
void FuncPtr(int *ptr) {
if (ptr != nullptr) {
cout << *ptr;
}
}
void FuncReference(int &ref) {
cout << ref;
}
- 指针存放的是地址,指针可以被重新赋值,可以在初始化时指向一个对象,在其它时刻也可以指向另一个对象,而引用非常专一,它会从一而终,它总是指向它最初代表的那个对象。再举个例子,有一个人叫特朗普,为他起个引用别名叫历史上最傻吊的总统,这个引用一定会从一而终,即无论什么时候,历史上最傻吊的总统一定是特朗普。而美国总统可以当作个指针,一段时间可以是奥巴马,过一段时间可以是特朗普,再过一段时间可能是nullptr。
指针和引用的使用场景 #
引用的主要功能就是作为函数的参数和返回值,看一段代码: #
struct A {
int a;
};
void func(const A &a) {
cout << a.a;
}
vector<int> vec(10);
vec[3] = 3;
为什么通过 vec[3] = 3
可以改变 vector 容器的值,因为 [] 操作符返回的就是引用,相当于为内部的变量起了一个别名,这里还可以让 [] 操作符返回一个指针,即 *vec[3] = 3
,这是不是有点丑,而且不符合语法需求。
其实我们平时编程过程中可能也注意到,实际上引用可以做的事情指针都可以做,但为什么还要引用这个东西?
答案:
用恰当的工具做恰如其分的工作,指针可以毫无约束的操作内存中的任何东西,功能十分强大,但是也很危险,所以可以在恰当的时机使用引用,当你需要指向某个东西,而且一定专一,绝不会让其指向其它东西,例如有些函数参数为了避免拷贝可以使用引用,或者实现一个操作符而其语法需求无法由指针达成,例如 vec[3] = 3
,可以使用引用,其它任何时候,都要使用指针。
最佳实践建议 #
使用引用的场景:
- 函数参数需要避免拷贝开销时
- 函数返回值需要链式调用时
- 运算符重载时
使用指针的场景:
- 需要表示空值的情况
- 需要改变指向对象的情况
- 需要多态性的情况
选择原则:
- 能用引用就用引用
- 必须用指针的场合才用指针
- 保持代码的简洁性和安全性