对引用的理解首先是根据《C++ primre》第五版中的解释,其次是我个人根据网上的资料,对C++实现引用本质的理解。下面分为两部分,首先理解引用体现出来的性质,其次是关于引用的具体实现。
1.引用具体表现行为
a.引用的定义:“引用为对象起了另外一个名字,引用类型将引用另外一种类型” —— 《C++ primer》5
也就是说引用本身不是一个对象,而是其初始值的一个别名。
b.引用的行为:
1)程序将引用与其初始值进行绑定,而不是将初始值拷贝到引用
2)一旦初始化完成,引用将与其初始值一直绑定在一起,不能改变
3)引用必须在定义时初始化,因为一旦初始化完成,将不能改变
4)引用只能绑定到对象上,不能绑定到字面值常量或表达式的计算结果上
5)由于引用不是对象,因此不能定义引用的引用,也不能定义引用的指针
c.引用与指针的区别:
1)引用不是一个对象,而指针是对象
2)引用必须在定义时初始化,而指针可以不用,使得 指针的可以被赋值和拷贝,而引用不可以
2.引用的实现
在一篇很好的博文中看到,认为引用是一个const 的指针,且对该指针的操作都会被编译器使用其解引用来替换,而引用对象本身是占内存的,只是体现得好像它仅仅是一个别名,但不存在内存空间一般。
具体如下代码分析:
int i = 1;
int &j = i;
cout << i << " " << j << endl; // 1 1
cout << &i << " " << &j << endl; //eg: 0x88888888 0x88888888
经过编译器进行替换后,上述操作变为:
int i = 1;
int *const j = &i;
cout << i << " " << *j << endl; // 1 1
cout << &i << " " << &*j << endl; //eg: 0x88888888 0x88888888
因此,就不难理解为什么对引用对象进行操作会出现上述的情况,且分析以下引用的各项要求与 T* const 之间的关系:
1)程序将引用与其初始值进行绑定,而不是将初始值拷贝到引用 —— 将对象的地址放入引用对象中,并没有产生拷贝
2)一旦初始化完成,引用将与其初始值一直绑定在一起,不能改变 —— T * const 对象的值不能改变
3)引用必须在定义时初始化,因为一旦初始化完成,将不能改变 —— const 对象的值必须在初始化时定义
4)引用只能绑定到对象上,不能绑定到字面值常量或表达式的计算结果上 —— 字面值常量可看作临时对象,将指针指向临时对象不安全
5)由于引用不是对象,因此不能定义引用的引用,也不能定义引用的指针 —— 若允许引用的引用,则对引用的一次解引用将不能获取真实想绑定对象的值,不符合引用想要体现的特性,分析代码如下:
int i = 1;
int * const p = &i; // int & p = i;
int *const *const pp = &p; // int & pp = p;
cout << *p << *pp << endl;
//希望的输出结果:
cout << i << i << endl; //希望均是绑定到 i
//事实上输出结果等效于:
cout << i << p << endl; //输出与预想不符
struct ref
{
int & i;
};
int main()
{
cout << sizeof(ref) << endl; //输出4
}
可以看出引用并不单单是一个别名,而是占用内存空间的。个人理解引用是一个 const 类型的指针(注意:不是指向const 对象的指针)。
可以参见一个翻译得很好的博客:点击打开链接