admin 管理员组文章数量: 888297
C++ 引用(左值引用)
目录
1、引用的定义
2、引用的特点
3、引用作为形参代替指针
4、const与引用
4.1、const int&
4.2、int& const
5、其他使用方式
5.1、&与数组
5.2、&与指针
6、引用与指针的差别
7、不可以对函数中的局部变量或对象以引用、指针的方式返回
8、&实质: int &p = a; <=> int *const p = a;
9、分析效率 函数形参:变量、指针、引用
1、引用的定义
引用的定义:类型& 引用变量名称 = 变量名称;
这就是引用变量的定义。&和类型结合称之为引用符号,不是取地址符,代表引用的意思。
int main()
{int a = 0;int b = a;//变量int &c = a;//引用return 0;
}
此时,c 就是 a 的别名,c等价于a:c就是a,a就是c ,就像鲁迅就是周树人,周树人就是鲁迅。
此时,若将c的值改为10,也就是将a的值改为了10。
引用的大小,也就是其所指变量的大小:
2、引用的特点
引用有如下特点:
1、引用必须初始化。
2、没有空引用。
3、没有引用的引用(二级引用、多级引用)。
4、一个引用一旦被初始化为一个变量的别名,就不能更改、再成为其他变量的引用(别名)。
若存在二级引用 "int&& n = m;",由于 m 与 a 等价,则存在 "int&& n = a;",这显然是不成立的。
3、引用作为形参代替指针
在学习引用之前,在C语言基础上,假设我们要编写一个两值交换函数 swap(a,b) ,由于需要实现对形参a、b所传入的值进行交换,继而影响实参的两值进行交换,所以,我们需要将实参两变量的地址(指针)传入函数,进行解引用交换。
void swap(int* a,int* b){//对于一切传入函数的指针,先进行安全性检查assert(a!=nullptr&&b!=nullptr);int tmp=*a;*a=*b;*b=tmp;
}
因为引用是原变量的别名,等价于原变量,如果我们将形参换位两变量的别名,也可以实现在函数中两值交换,影响原变量值交换。
void swap(int& p,int& q){int tmp=p;p=q;q=tmp;
}
此时的代码更简洁,而且因为形参是引用,不需要进行判空(没有空引用)。
4、const与引用
4.1、const int&
const 在修饰引用时,只有一个作用,就是限制该引用(别名)只能读,不能写(修改)。
若一个变量被 const 修饰为常变量,则其引用也必须被const修饰为常引用。
添加const后正确。
" const int& 引用变量名称 " 也被称为万能引用,可以成为任何相同类型变量的引用。但也就不能通过引用改变其对应变量的值。
" const int& u = 100; " 的实质为 系统开辟临时变量tmp用于保存100,u再成为变量tmp的别名。
4.2、int& const
若 const 在 & 后面(右边)(int& const p = a;),则与const在修饰指针时一般,此时const修饰引用p不能再成为别人的引用,但引用本身就有只能绑定一个变量的特点,所以 & 右边的 const 是无效的,编译时被忽略。
5、其他使用方式
5.1、&与数组
若此时定义有数组arr{ 1,2,3,4,5 },我们可以通过 "int& p = arr[i];" 的方式,为数组单个元素添加引用。
此时,p是arr[0]的引用,p的地址就是arr[0]的地址。
若要为整个数组添加引用,不能直接使用 "int& q = arr;" 的方式,此时arr代表整个数组,是 int[5] 类型的,我们也要以 int(&)[5] 类型为 arr 添加引用。
此时,q就等价于arr,q与arr地址相同,可以通过 q[i] 来访问数组arr第 i 个值(0=<i<5)。
sizeof(q) 的大小等于 sizeof(arr) 的大小,也就是数组的大小。
5.2、&与指针
int main()
{int a = 10, b = 20;int* p = &a;//指针p指向aint*& rp = p;//rp为指针p的引用//rp等价于prp = &b;//修改rp的指向为breturn 0;
}
如图,a的值为10,a的地址为 0x0096f7d4,此时,指针p与引用指针rp的值都为变量a的地址,解引用后都是a的值10,p与rp的地址也相同。
在执行" rp = &b; "后,p的指向被修改为变量b,值为b的地址,解引用后为b的值20。
6、引用与指针的差别
指针 | 引用 | |
---|---|---|
1 | 从语法规则上讲,指针变量存储某个实例(变量或对象)的地址; | 引用是某个实例的别名。 |
2 | 程序为指针变量分配内存区域; | 而不为引用分配内存区域,别名的内存地址是其所指实例的地址。 |
3 | 指针在访问其所指变量时需要解引用; | 引用可以直接使用。 |
4 | 指针变量的值可以改变,存储不同实例的地址(修改指向); | 一个引用在定义时就被初始化,绑定为一个实例的引用,无法在成为其他实例的引用。 |
5 | 指针变量的值可为空(NULL,nullptr); | 没有空引用。 |
6 | 指针变量在作为形参时需要测试它的合法性(判空); | 引用不需要判空。 |
7 | 对指针变量使用"sizeof"得到的是指针变量的大小; | 对引用使用"sizeof"得到的是其对应变量的大小。 |
8 | 理论上指针的级数没有限制; | 引用只有一级,不存在引用的引用。 |
9 | ++指针会使指针变量指向下一个实例的地址,而不是修改所指实例的内容; | ++引用会直接影响到引用对应的实例。 |
7、不可以对函数中的局部变量或对象以引用、指针的方式返回
int add(int a,int b){int c = a + b;return c;
}
这是一个简单的求和函数,在main函数中执行到这个函数时,系统会为这个函数开辟栈帧,供add函数去执行;执行完成后,用一个临时变量保存返回的数据值,然后回收为add开辟的栈帧。
int* funa() {//errorint a = 10;return &a;
}
int& funb() {//errorint a = 10;return a;
}
假如这两个函数在程序中运行。通过上面的学习可以知道,引用的地址是其对应变量的地址,而指针也保存的其指向变量的地址。
上面funa函数返回的指针是funa函数运行时产生的局部变量的地址。在函数运行结束后,栈帧被回收,这段栈帧就不能被随意访问了,但此时通过函数的返回值,我们还拥有这段栈帧中的一个地址,就可以解引用访问,但这是非法的,所以funa是错误的。
funb函数返回的是其栈帧中变量a的别名,所以这个别名也就是funb栈帧内存中的局部变量。在funb运行结束后,栈帧被回收,其中全部局部变量死亡,但我们可以通过返回值去访问原funb栈帧中内存地址的变量,这也就是非法的。
8、&实质: int &p = a; <=> int *const p = a;
在语法上,引用本质上是一个指向其对应变量的指针常量:int &p = a; <=> int *const p = a;。在使用时,我们只需像使用普通变量般使用它,无需解引用等操作,系统在编译运行时,自动将使用引用的地方进行了如下替换。
所以,在编译阶段,指向一个变量的指针和其引用初始化的代码才会一模一样。
所以,引用原本就被const修饰为常变量,这也是一个引用只能绑定一个实例的原因。
9、分析效率 函数形参:变量、指针、引用
struct Student {char s_id[20];char s_name[20];char s_sec[10];int s_age;
};void funa(struct Student s1) {}//值传递
void funp(struct Student* sp){} //指针传递
void funb(struct Student& s2) {}//引用//底层是指针
对于值传递,只在实参初始化形参时,访问实参一次内存;对应指针传递,在使用实参地址初始化形参时,访问实参一次内存,再至少在使用一次实参的值时访问一次实参地址,总共至少访问两次实参地址;对于引用做实参,在实参初始化引用形参时,访问实参一次内存,也再至少在使用一次实参的值时访问一次实参地址,总共至少访问两次实参地址。
所以,在参数字节大小较小,且无需通过形参改变,影响实参的值时,尽量使用值传递的方式,效率高;在参数较大,或需通过形参改变,影响实参的值时,使用指针与引用效率相当,但引用使用更简单。
本文标签: C 引用(左值引用)
版权声明:本文标题:C++ 引用(左值引用) 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/free/1699078876h326825.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论