关于引用的思考
什么是引用
引用是一种自动解引用的常指针。由于它是一个常指针,所以必须在定义的时候就进行初始化。同样由于它是常指针,所以我们不能改变其指向,也不能使用它来迭代访问数组、链表等。
必要性
首先,引用是C++对于指针的扩展,他们为什么要扩展呢?是因为指针不好用,还是因为有些事指针做不来呢?因此我会讨论并弄明白,引用对于C++是否有必要性。
如果引用没有必要性,也就是说它能做的事,指针照样都能做,最多就是麻烦点呗。那么我们就可以断言结论:“尽量使用引用,只有在无法使用引用的地方才使用指针”,因为如果尽量使用指针,那么引用将失去其存在的意义。然而事实并非如此,引用对于C++是有必要性的,也就是说,有些地方只能使用引用。因此有了下图所示的局面。
那么有哪些地方只能使用引用呢?
答:拷贝构造函数,运算符重载函数的参数及返回值。
另外,为了讨论的完整性,以下是那些只能使用指针的情况:
- 你需要存在不指向任何对象的可能。
- 你需要能够在不同的时刻指向不同的对象。
对于它们都可以处理的情况我们该作何选取呢?首先,让我们看一个例子:
int a;
... // 一些操作
func(a);
在C语言中,如果你看到如上代码,那么你可以断言,你的变量a不会被修改。然而在C++中如果使用了引用,那么你就不能如此确信了。因此引用的出现在这种情况下破坏了C语言原有的阅读性。在C语言中如果一个函数想要修改你的变量,那么你需要显示地对你的变量取地址,另外,编译器也会帮你检查是否有对变量取地址。因此,至少在一般情况下,我强烈不建议使用引用。
所以我的结论是,能不使用引用就不使用引用。即让引用去只有它能去的地方,以前的事让以前的人来干。
细节
// 指针形式
void func(const int *a); // 函数声明
func(1); // 函数调用
// 引用形式
void func(const int &a); // 函数声明
func(1); // 函数调用
对于指针形式的调用,编译器将会报错:error: invalid conversion from 'int' to 'const int*'。但对于引用形式的调用,编译器就不会报错,而是创建一个临时变量来存储常量值,然后再将临时变量的引用传递给函数。对于上例中的引用形式,在实际中我们不会写出这样的代码,然而我们可能会写出如下模板,然后使用int将其实例化:
template <typename T>
class stack{
... // 其它内容
public:
bool push(const T& a)
{
... // 一些操作
}
};
stack<int> s;
s.push(1);
实际上stl就是这样的。
常见错误理解
- 对数组来说,引用可以保存数组的维数,而指针不能。
解释:这是因为你对指针还不够了解。 - sizeof(reference)得到的是所引用的变量(对象)的大小,而sizeof(pointer)得到的是指针本身的大小。
解释:那是因为你没有对指针解引用,sizeof(*pointer)得到就是所指向的变量(对象)的大小。你忘了即使是自动解引用也是解了引用的。