在C++ 11中,新增了一种引用(本文都指左值引用)。从作用上来讲,它和指针类似,都可以用来间接引用对象,他们之间到底有什么异同呢?
在分析之前我们先介绍一下引用。引用为对象起了一个别名。例如:
int a = 1024;
int &b = a;
这里的b就是指向a,相当于a的另外一个名字。b是一个引用,但并非一个对象,同时引用的类型都要和对象的类型严格匹配(在具有继承关系的类中是例外,这里不展开),因此有下面的操作:
int a = 1024;
int &b = a;
int &c = b; //非法,b在此之前已经是引用,不能定义引用的引用
int &d = 1024; //非法,引用类型的初始值必须是对象,而不能是字面值
const int &e = 1024 //合法,e为常量引用,只要能够转换成引用的类型,可以使用任何表达式作为初始值
double f = 1.024;
int &g = f; //非法,类型不匹配
而对引用赋值,实际上是将值赋给了与引用绑定的对象,例如:
int h = 1024;
int &i = h;
i = 256;//将256赋给了h
同时,引用必须被初始化,下面的操作是非法的:
int &j;//非法,没有初始化
学习过C的朋友可能对指针已经比较熟悉了,这里不占用较大篇幅。它存储的是一个地址,地址指向的是一个对象。例如:
int a = 0; //定义变量a
int *ptr = &a;定义int类型指针ptr,它存放变量a的地址
与引用类似,指针也实现了对其他对象的间接访问。同样的,它也要求指针的类型和指向的类型严格匹配。
例如:
int &a; //非法,必须被初始化
int *p;//合法,但如果是非静态的指针变量,将拥有一个不确定的值
声明一个指针变量而不初始化是合法的,但是不建议这么做,原因可参见C语言入坑指南-被遗忘的初始化。正因如此,使用引用而非指针作为函数的参数是一个不错的选择,因为引用永远不为空,函数入口也就不需要做过多的检查,引用也就更富效率。
引用不是对象,它不能被再次赋值,而指针是可以的。例如:
int a = 1024;
int b = 2048;
int &c = a;
int *d = &a;
c = b; //其引用不会被再次赋值
d = &b;//合法,指针可以指向另外一个对象
也就是说,一旦引用了引用,就没法让它绑定到其他的对象。看起来引用还挺从一而终啊。
另外,可以有指向指针的指针,而不存在引用的引用。因为引用不是对象。
int a = 1024;
int &b = a;
int &&c = b;//非法
int *d = &a;
int **e = &d;//合法
定义一个指针的时候,编译器为他分配内存,而引用不会单独分配空间。 引用所代表的就是最初绑定的那个对象,因此使用sizeof分别作用于引用和指针时,前者得到的是引用所绑定对象大小,而后者得到的是指针占用空间大小(4或8字节),例如在64位的程序中:
/**假设有以下结构*/
typedef struct INFO
{
int a;
int b;
char c;
}INFO;
INFO info = {0};//定义结构info
INFO &ref = info;//定义引用ref
INFO *ptr = &info;//定义指针ptr
sizeof(ref); //大小为INFO结构体占用空间大小,即12字节
sizeof(ptr); //占用大小为指针占用空间大小,即8字节
例如:
int a = 1024;
int &ref = a;
int *p = &a;
ref = 10; //使用引用可直接使用,将所绑定对象的值修改
*p = 11;//使用指针需要解引用
指针作为参数时,看起来是地址传参,实际上仍然传值,即将指针的一个拷贝作为实参,而由于指针指向的是一个对象,因此在函数内可以实现对指针所指向对象的内容进行改变。而引用作为参数时,实际上传递的对象本身,但又不需要拷贝,因为引用绑定的就是对象。
从前面的内容来看,除了使用方法的差异意外,引用似乎像是弱化版的指针,是不是有点像指针常量呢?当然了,当你知道需要指向某个东西,而且绝不对改变其指向时,引用是一个不错的选择。