引用类型是C++11标准中新增的一种复合类型,作用是为对象起了另外一个名字。
//引用i变量,起名为j
int i = 0;
int &j = i;
//这样也可以
int i = 0, &j = i;
*需要注意的是
1.引用类型在定义时必须进行初始化
int &j;//错误
2.引用的类型和初始化的类型必须严格匹配
double i = 0.0;
int &j = i;//错误
3.引用类型初始值必须是一个对象,而不能是一个常值
int &j = 0;//错误
4.引用类型本身不是一个对象,不能定义引用的引用,这一点和指针不同。
int *p1 = nullptr,**p = &p1;//二级指针正确
int i=0,&j=i;
int &&k = i; //错误
5.引用类型定义及初始化后无法再改变,对它赋值、取值都是对所引用的变量赋值、取值(包括sizeof等)
sizeof(int&);//4
sizeof(char&);//1
sizeof(double&);//8
6.引用类型所占空间大小和指针相同(与字长对应,x86 是 4,x64 是 8)
7.可以使用指针类型的引用,但不允许使用指向引用的指针
int i = 0;
int *p = &i, **ps = &p;
int &*j = nullptr;//对引用类型的指针,错误
int *&q = p;//指针类型的引用,正确
int **&qs = ps;//二级指针的引用,正确
8.*对const对象的引用,不能给引用进行赋值操作
const int i = 1;
const int &j = i;//正确
j = 2;//错误,常量引用不能赋值
int &k = i;//错误,非常量引用不能初始化为常量
常量引用特殊的地方在于它的初始化可以是任何表达式,只要该表达式的结果可以转换成对应常量类型即可。
int i1 = 1; const int &j1 = i1;//正确
double i2 = 1.5; const int &j2 = i2;//正确,初始化为1
const double i3 = 1.5; const int &j3 = i3;//正确,初始化为1
const int &j4 = 1;//正确
const int &j5 = 1.5;//正确,初始化为1
由于常量引用可以引用非常量对象,所以可以通过改变所引用的非常量对象改变常量引用,但不可通过常量引用来改变。
int i1 = 1; const int &j1 = i1;
j1 = 10;//错误,常量引用不可赋值
int i1 = 1; const int &j1 = i1;
std::cout << i1 << "," << j1 << std::endl;//1,1
1 = 10;
std::cout << i1 << "," << j1 << std::endl;//10,10
int &j2 = i1;
j2 = 100;
std::cout << i1 << "," << j1 << "," << j2 << std::endl;//100,100,100
然而对于其他类型的对象来说,就不会因原变量而改变,如double类型。
double i1 = 1.8; const int &j1 = i1;
std::cout << i1 << "," << j1 << std::endl;//1.8,1
i1 = 10.8;
std::cout << i1 << "," << j1 << std::endl;//10.8,1
double &j2 = i1;
j2 = 100.8;
std::cout << i1 << "," << j1 << "," << j2 << std::endl;//100.8,1,100.8
这是因为int常量引用在定义初始化时得到的引用的是编译器创建的一个临时量,而并非是double类型的i1。
double i1 = 1.8;
const int x = i1;//编译器编译时创建的临时量,x=1
const int &j1 = x;
--->一个常见的用法是用在函数传递参数。
#include<iostream>
void swap1(int *a, int *b)
{
*a += *b;
*b = *a - *b;
*a -= *b;
return;
}
void swap2(int &a, int &b)
{
a += b;
b = a - b;
a -= b;
return;
}
int main(int argc, char* argv[])
{
int x(0), y(1);
swap1(&x, &y);
std::cout << x << "," << y << std::endl;//1,0
swap2(x, y);
std::cout << x << "," << y << std::endl;//0,1
return 0;
}
用指针和引用都可以完成对实参的改变,引用要方便许多。函数的形参是实参的引用,直接使用就可以,不用再麻烦的取地址。这样以来,想在函数里操作实参的就加引用就可以,指针同样可以用 *&p 作为形参来操作实参。
还需要注意的是,不可创建数组名的引用,因为数组名并不是一个对象。所以需要将数组作为函数参数时,建立一个指向数组的指针即可。
#include<iostream>
void func(int *&a)
{
a[0] = 10, a[1] = 20;
return;
}
int main(int argc, char* argv[])
{
int a[2] = { 1,2 };
std::cout << a[0] << "," << a[1] << std::endl;//1,2
int *p = a;
func(p);
std::cout << a[0] << "," << a[1] << std::endl;//10,20
return 0;
}
END