目录
什么是引用:
引用的例子
引用的特性:
常量引用与非常量引用
使用场景
引用与指针的区别
什么是引用:
引用的概念
引用实际上就是取别名,提起这个名字就会让人知道是谁,谈起“鸡哥”就知道是坤坤,那么我们所说的引用变量,不是新定义了一个变量,而是给一个已经定义的变量取别名,编译器不会为引用变量开辟空间,它和被引用的实体共用一个空间,,
现有变量a,b是a的引用,a与共用一个空间,那么对a进行操作就相当于也会对b进行操作反之亦然
引用的例子
类型& 引用变量名 = 引用实体;
在这里&不是取地址符,而是类型标识符的部分,在这个测试代码中b是a的引用,根据上面的概念它们的值和地址应该是相同的,我们可以看一下结果:
int main()
{int a = 10;int & ra = a;cout<<a<<" "<<ra<<endl;cout<<&a<<" "<<&ra<<endl;system("pause");return 0;
}
运行结果:
结果确实和概念所说的一样,但是这其中还有我们所需要在了解的,a和b的地址相同,我们知道在a变量定义的时候会为它随机分配内存,但是引用变量b不同编译器不会为它分配内存,即引用变量b是根据自己即将引用的变量的地址分配的,所以在引用变量b定义时,必须初始化,而不能先声明,在赋值。也就是定义时就指向另一个已经定义了的变量,否则,引用变量将无法得知自己的地址,所以 int& b;这是错误的
注意:引用变量必须和引用的实体是同种类型
引用的特性:
1. 引用在定义时必须初始化
在上面我们已经提到了,引用在定义是必须初始化,我们来测试一下
2. 一个变量可以有多个引用
就像一个人可以有多个别名,变量也可以有多个引用
int main()
{int a = 10;int& ra = a;int& raa = a;cout<<a<<" "<<ra<<" "<<raa<<endl;cout<<&a<<" "<<&ra<<" "<<&raa<<endl;return 0;
}
运行结果:
与上面的没有区别,它们的值和地址都是相同,
3. 引用一旦引用一个实体,再不能引用其他实体
引用变量在引用某个变量后,在它的生命周期内,就不会成为其它变量的引用
举个栗子:定义变量a ,b是a的引用,定义变量c尝试是否可以使b引用a,(引用的特征:共用一块空间 那么我们看它们的地址,判断是否成功引用)
int main()
{int a = 10;int& ra = a; int c = 100;cout<<a<<" "<<ra<<" "<<c<<endl;ra = c;cout<<a<<" "<<ra<<" "<<c<<endl;cout<<&a<<" "<<&ra<<" "<<&c<<endl;system("pause");return 0;
}
运行结果:
结果是b并没有成为c的引用,ra = c;不会成功,只会实现赋值的效果
常量引用与非常量引用
我们上面说谈的都是非常量引用,非常量引用要求被引用的实体必须有左值(个人理解的左值:地址值),若引用的实体没有左值编译器会报错,
常量引用 :常量引用也就是 const 类型& 引用变量名 = 引用实体 这里的引用实体常是常规常量、表达式,左右两侧的类型不同
int main()
{//int& a = 10; 编译报错 10 是常量const int& ra = 10;const int b = 10;//int& rb = b; 编译报错 b是一个常量const int& rb = b; int aa = 10;int bb = 20;int cc = aa + bb;//int& rcc = aa+bb;//编译报错 非常量引用的初始值必须为左值const int& rcc = aa + bb;system("pause");return 0;
}
常量引用创建临时变量
由于类型不同,所以只能使用常量引用来引用a,但是这和非常量引用不同,ra和a不共用一个空间,编译器会创建一个于ra类型相同的临时的无名变量并初始化并将a赋值给它,,然乎ra就是这个无名变量的引用
int main()
{double a = 13.4;const int& ra = a;cout<<a<<" "<<ra<<endl;// a = 13.4 ra = 13a = 20.6;cout<<a<<" "<<ra<<endl; //a = 20.6 ra = 13cout<<&a<<" "<<&ra<<endl; system("pause");return 0;
}
使用场景
做参数
C语言中给函数传参有两种方法:传值和传地址,在C++中有了第三种方法:传引用
void Swap(int& left, int& right){ int temp = left; left = right; right = temp;
}
做返回值
int& add(int a, int b)
{int c = a+ b;return c;//return a + b// 编译报错 a+b不是左值 (表达式 常量 没有左值)
}
int& Count()
{static int n = 0;n++;return n;
}
int main()
{int& ret = add(1,2);//不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,//因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。int& r = Count();cout<<r<<endl;cout<<r<<endl;cout<<r<<endl;system("pause");return 0;//如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,//如果已 经还给系统了,则必须使用传值返回。}
以引用返回函数值,定义函数时需要在函数名前加 &
不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。
如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,
如果已经还给系统了,则必须使用传值返回。
引用与指针的区别
在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。但是在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
通过汇编语言我们可以发现 它们的底层实现是完全相同的,引用的实现也是借用指针(先取地址,在解引用)
引用于指针的不同
1 引用变量在定义时必须初始化,而指针不用;
2 引用变量在引用一个实体后就不能引用其他实体,而指针在任何时候都可以指向一个同类型的实体
3 引用必须指向有效的空间,指针可以指向NULL
4 sizeof()中的含义不同:引用结果为引用类型的大小,指针始终是地址空间所占字节个数
5 引用不能引用数组,指针可以
6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
7 有多级指针,但是没有多级引用
8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
9 引用比指针使用起来相对更安全,没有野指针的危险