引用是C++中一个很重要的概念。英文为reference,引用定义了对象的一个别名。当对象的值修改时,引用会发生对应的改变;当引用的值发生改变时,被引用的对象的值也发生变化,因为对象和对象的应用操作的是同一地址单元。
1、引用的定义
使用int & a = b;即可定义整型变量b的引用a。
(1)由于引用是对象的别名,因此引用在定义的时候就必须初始化。int &c;这种写法是不合法的。
(2)非const引用在定义时必须使用同类型的对象初始化。int &c=42;这种写法是不合法的。double a=3.14; int &b=a;这种写法也不合法,因为引用是int的,而初始化使用的是double类型的,类型不匹配。
2、常引用(const reference)
const引用有较多使用。它可以防止对象的值被随意修改。因而具有一些特性。
(1)const对象的引用必须是const的,将普通引用绑定到const对象是不合法的。这个原因比较简单。既然对象是const的,表示不能被修改,引用当然也不能修改,必须使用const引用。实际上,const int a=1; int &b=a;这种写法是不合法的,编译不过。
(2)const引用可使用相关类型的对象或右值初始化。这个是const引用与普通引用最大的区别。const int &a=2;是合法的。double x=3.14; const int &b=a;也是合法的。
3、常引用的原理
const引用的目的是,禁止通过修改引用值来改变被引用的对象。const引用的初始化特性较为微妙,可通过如下代码说明:
double val = 3.14;
const int &ref = val;
double & ref2 = val;
cout<<ref<<" "<<ref2<<endl;
val = 4.14;
cout<<ref<<" "<<ref2<<endl;
上述输出结果为3 3.14和3 4.14。因为ref是const的,在初始化的过程中已经给定值,不允许修改。而被引用的对象是val,是非const的,所以val的修改并未影响ref的值,而ref2的值发生了相应的改变。
那么,为什么非const的引用不能使用相关类型初始化呢?实际上,const引用使用相关类型对象初始化时发生了如下过程:
int temp = val;
const int &ref = temp;
如果ref不是const的,那么改变ref值,修改的是temp,而不是val。期望对ref的赋值会修改val的程序员会发现val实际并未修改。
4、(int &)
说到引用就需要提一下(int &),这是一个强制的类型转换。几年前一个非常有名的笔试题,简写在这里
float a = 1.0;
cout<<(int &) a<<endl;
结果是1065353216。为什么呢?
需要理解(int &)a表示的是什么。变量a是浮点类型的,(int &)a表示将浮点类型变量a强制转换成int类型的引用。虽然普通整型引用无法用非整型变量初始化,但是C++却提供了引用的强制类型转换。
引用是变量的别名,与变量指向同一内存地址。现在变量是浮点型,使用浮点类型的格式存储,在C++中是IEEE 754标准,使用强制类型转换,将转换为一整型引用,即是将其浮点类型的存储方式按照整型的存储方式进行解读。
1.0f的IEEE 754浮点表示为0011 1111 1000 0000 0000 0000 0000 0000,按照整型变量的存储格式解释为2^29+2^28+2^27+2^26+2^25+2^24+2^23=10 6535 3216。
(int&) a的一种等价的写法是*((int*) &a),即取a的地址&a,将其强制转换为int指针类型,即表示将该地址存储的是int类型,地址未发生变化,但是地址类型由浮点类型变为整型,然后取该地址的内容,由于该地址是int类型,因此就按照int类型的存储方式解读了。下面的代码可以证明这一点:
float a = 1.0f; // 浮点数
cout<<&a<<endl; //输出a的地址
cout<<(int &)a<<endl;
int * ptr = (int *) &a;//ptr指向a
cout<<ptr<<endl; //输出ptr,即a的地址
cout<<*ptr<<endl; //输出ptr指向的内容
上述代码段中,&a和ptr都表示变量a的地址,不同之处在于,&a表示a是浮点类型变量,ptr表示a是整型变量,因此*(&a)和*(ptr)不同,而(int &)a与*(ptr)相同。
上述方法可以用来查看机器中浮点数的存储机制,因为整型变量的存储方式总是相同的,而浮点数的存储则可能存在差异性。
参考自:《C++ Primer》
——The End——