引用是一种新的数据类型. &叫做"引用的标记"
传值与传址,传值的话生成一份实时的拷贝
传值:效率低,不能通过形参改变实参,但是也是优点,不会改变实参
传址:效率高,能通过形参改变外部实参,但是不安全,指针要判空,可读性差
传引用比传值快很多很多
传址和传引用差不多,都非常快.
传址和传引用的反汇编码一模一样
引用的特性:
0.引用类型必须和引用实体同种类型
int a = 10;
double& ra = a;//报错
1.引用必须初始化
int a = 10;
int& ra;//名字给谁取的不知道了.
2.一个变量可以有多个引用.多个绰号.变量变了,引用全都变
rra = 2000;//那么ra和a都成了2000
3.引用后不能再引用别的实体.
前提:引用的生命周期在一个{}内.出了生命周期就可以重新给其他实体引用了.
4.引用只能引用变量
const int a = 10;
int& ra = a;//报错,a是常量,不是变量.
int& ra = 10;//报错
const int& ra = 10;//通过!加const可以引用常量.
5.常引用:
double d = 12.34;
int& rd = d;//不行,不是同种类型.但是:
const int& rd = d;//可以,rd值为12,但是如果再令:
d=3.14;//此时d内容已经改变,rd内容还是12.
这个过程中rd和d不再共用同一段内存,另立门户,也就是说const这样操作的话rd不是d的别名.rd是开辟的另一段临时空间的别名,但是地址又不确定,这段空间也没有名字.
也正因为临时空间具有常性,是常量,所以要加const.
引用的应用:
结构体里ps.array很繁琐,可以:
int& ra =ps.array;//以后就用ra了,很方便.
void Swap(int& left,int& right)
这样的话left/right就不再是实参的一份拷贝,而是实参的引用,是别名.实参能够成功交换
可以这么用:
void Print(const int& ra){//加const更安全,不让形参改变实参---想修改,不加;不想修改,加.
cout<<ra<<endl;
}
int main(){
Print(a);
}
↑↓
引用作为函数的参数:
1.需要通过形参改变外部实参 T& (T指任意类型)
2.不需要通过形参改变外部实参 const T&
引用作为函数的返回值:如果出了作用域返回的对象已经不在了,不能引用,返回引用本质是返回别名,这个对象已经不在了,已经还给系统了,此时引用就类似野指针~
注意不能返回函数栈上的空间
只要返回值的生命周期不受函数控制(函数结束后不影响变量的生命周期),如:
1.全局变量
2.static类型的变量
3.返回引用类型的变量
引用在底层实际就是按照指针的方式处理
实际上:引用类型的变量也有空间,空间里放实体的地址
在概念层面:引用与实体共用同一块内存空间,引用是实体的别名
T&=T* const指针指向不能变----这也是为什么不能成为别的实体的引用的原因
const T&=const T* const它的指向和指向的内容都不能变
引用和指针的不同点:
- 引用在定义时必须初始化,指针没有要求
- 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
- 没有NULL引用,但有NULL指针
- 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
- 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
- 有多级指针,但是没有多级引用
- 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
- 引用比指针使用起来相对更安全
数组的引用:
int a[10];
int (&ra)[10] = a;//要括起来.
ra[0] = 100;