C++笔记梁哥day3
引用(reference)
引用是C++对C的重要扩充。在c/c++中指针的作用基本都是一样的,但是C++增加了另外一种给函数传递地址的途径,这就是按引用传递(pass-by-reference),它也存在于其他一些编程语言中,并不是是C++的发明。
变量名实质上是一段连续内存空间的别名,是一个标号(门牌号),程序中通过变量来申请并命名内存空间,通过变量的名字可以使用存储空间
对一段连续的内存空间只能取一个别名吗?c++中新增了引用的概念,引用可以作为一个已定义变量的别名。基本语法: Type& ref=val;
注意事项:&在此不是取地址运算,而是起标识作用。类型标识符是指目标变量的类型,必须在声明引用变量时进行初始化。引用初始化之后不能改变。不能有 NULL 引用。必须确保引用是和一块合法的存储单元关联。可以建立对数组的引用。
一、引用的初始
语法:
- &和别名结合,表示引用
- 给某个变量取别名,就定义某个变量
- 从上往下替换
- 赋值
1
2
3
4
5
6
7
8
9
10int num = 10;
//现在要给num定义一个引用a
//第一步 &和a结合
&a
//第二步 给num取别名,定义num
int num;
//第三步 从上往下替换
int &a;
//第四步 赋值
int &a = num;a完全等价于num,操作a就是操作num。
注意:引用必须初始化,一旦初始化就不能再次修改。
1 | int num = 10; |
二、引用作用于数组
1、方法一:直接替换法
1 | int arr[5] = {1,2,3,4,5}; |
运行结果:
2、方法二:配合typedef
先用typedef
给类型取个别名,在使用类型的别名定义变量,接着再给变量定义引用
1 | void test02(){ |
运行结果:
三、引用作为函数的参数
1 | //交换失败 |
运行结果:
四、引用作为函数的返回值
不要返回局部变量的引用
1
2
3
4
5
6
7
8int& my_data1(){
int num = 20;
return num;
}
void test04(){
int &ret = my_data1();
cout<<"ret="<<ret<<endl;//非法访问内存
}运行结果:
如果返回局部变量的引用是很危险的,当函数执行完后,局部变量已经被释放,这个时候你去操作它就是操作非法空间。可以返回静态局部变量的的引用,它的生命周期比较长
1
2
3
4
5
6
7
8
9
10int& my_data2(){
static int num = 20;
return num;
}
void test05(){
int &ret = my_data2();
cout<<"ret="<<ret<<endl;
ret = 200;
cout<<"ret="<<ret<<endl;
}运行结果:
当函数返回值作为
左值
时,那么函数的返回值的类型必须是引用
1
2
3
4
5
6
7
8
9
10int& my_data3(){
static int num = 50;//static定义的变量只会初始化一次
cout<<"num = "<<num<<endl;
return num;
}
void test06(){
//函数的返回值 作为左值
my_data3() = 2000;//调用函数,同时将2000赋给了函数的返回值
my_data3();
}运行结果:
五、引用的本质是常量指针(了解)
引用的本质在C++内部实现是一个指针常量.
1 | Type& ref = val;//Type* const ref = &val; |
C++编译器在编译过程中使用常指针作为引用内部实现,因此引用所占用的空间大小与指针相同,只是这个过程是编译器内部实现,用户不可见。
1 | int data = 10; |
六、指针的引用
1 | void func1(char **p){ |
运行结果:
七、常引用
需求:定义一个函数需求遍历一个结构体
- 直接传入结构体运行结果:
1
2
3
4
5
6
7
8
9
10
11
12struct STU{
int num;
char name[32];
};
void myPrintSTU1(STU stu){
cout<<"sizeof(stu)="<<sizeof(stu)<<endl;
cout<<"num = "<<stu.num<<",name = "<<stu.name<<endl;
}
void test08(){
STU stu = {100,"lucy"};
myPrintSTU1(stu);
}
函数myPrintSTU1
的缺点是形参占用空间太多
- 传入结构体的地址运行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16struct STU{
int num;
char name[32];
};
struct STU{
int num;
char name[32];
};
void myPrintSTU2(STU *stu){
cout<<"sizeof(stu)="<<sizeof(stu)<<endl;
cout<<"num = "<<stu->num<<",name = "<<stu.name<<endl;
}
void test09(){
STU stu = {200,"bob"};
myPrintSTU2(&stu);
}
函数myPrintSTU2
的解决了形参占用空间太多,在32位环境中只占用四字节,但是缺点是传入的是指针,这个函数只是用来遍历,读取操作,但是可以修改结构体stu里面的内容,这样很危险
2的优化
将形参加了const修饰1
2
3
4
5
6
7
8
9void myPrintSTU3(STU const *stu){
cout<<"sizeof(stu)="<<sizeof(stu)<<endl;
//stu->num=2000;不能修改 会报错
cout<<"num = "<<stu->num<<",name = "<<stu->name<<endl;
}
void test10(){
STU stu = {200,"bob"};
myPrintSTU3(&stu);
}采用引用的方法
1
2
3
4
5
6
7void myPrintSTU4(STU &stu){
cout<<"num = "<<stu.num<<",name = "<<stu.name<<endl;
}
void test11(){
STU stu = {300,"Lily"};
myPrintSTU4(stu);
}运行结果:
函数myPrintSTU4
的解决了形参占用空间太多,其不占用空间,这个函数只是用来遍历,读取操作,但是可以修改结构体stu里面的内容,这样很危险,所以就有了常引用
- 4的优化,使用常引用
1 | void myPrintSTU5(STU const &stu){ |
运行结果:
常量的引用
给常量取一个别名(定义一个引用)
会报错10的类型是const int,类型不匹配,系统怕你后面把常量10改了 num=20;
1
2
3
4void test13(){
int &num = 10;
cout<<"num = "<<num<<endl;
}运行结果:
1
2
3
4void test14(){
const int &num = 10;
cout<<"num = "<<num<<endl;
}运行结果: