C++引用的理解与实现原理
如何理解引用 1
在使用上,我们要把引用当成另一个对象的“别名”
即看到一个引用,我们可以直接把它脑补成原来的对象
这样会衍生出引用的一系列特性(与指针相比):
- 引用被创建时必须被初始化,也叫“绑定”(指针则可以在任何时候被初始化)
- 不能有NULL 引用,引用必须与合法的存储单元关联(指针则可以是NULL)
- 引用初始化后不能改变引用的对象(指针则可以随时改变所指的对象)
引用如何实现
实际上如果查看汇编代码的话,引用是通过指针实现的
int a = 55;
int &b = a;
int * const c = &a;
这一段代码会被编译器翻译为:
movl $55, -28(%rbp) // *(-28(%rbp)) =55; 即 int a = 55
leaq -28(%rbp), %rax
movq %rax, -16(%rbp) // -16(%rbp) = -28(%rbp); 即 int &b = a;
leaq -28(%rbp), %rax
movq %rax, -24(%rbp) // -24(%rbp) = -28(%rbp); 即 int * const c = &a;
可以看到虽然b是a的引用,但在实现上并不是简单的替换,而是生成了b的空间存储a的地址,和指针c的实现是相同的。即引用在实现上相当于是一个指针常量 。[指针常量与常量指针]
如何理解引用 2
我们可以看到,引用只是一种概念上的抽象,那在C++中为什么同时存在引用和指针呢?什么情况下使用引用呢?
结论1:C++的引用主要是为了支持运算符重载;指针的存在主要是为了兼容C语言。[为什么 C++ 有指针了还要引用?]
结论2:用户自定义类型最好用引用传参,这样可以避免不必要的构造函数和析构函数调用;对于内置(C-like)类型,按值传参会比按引用传参更高效。[内置类型传值效率高的原因]
引用的一些特性
有了这两层对引用的理解,我们就可以解释引用的下面这些特性了:
引用++
是原来的对象执行自增(引用是对象的别名)sizeof(引用)
的大小是被绑定的对象的大小(引用是对象的别名)- 一个类的成员变量带引用,此引用 必须显示在初始化列表初始化(引用在创建时必须初始化)
- 一个类的成员变量带引用,此引用 给类的大小贡献一个地址的长度(引用在实现上是指针)