引用(reference)就是C++对C语言的重要扩充。引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。引用的声明方法:类型标识符 &引用名=目标变量名。 我让林冲去招安和我让豹子头去招安的意思一样。
注意: 引用并没有开辟额外的空间,造成比必要的空间浪费。
- 引用在定义时必须初始化,且初始化指定后就不可修改,指针不必初始化,但可以改变指向
- 一个变量可以有多个引用。
- 引用一旦引用一个实体,再不能引用其他实体。
引用的具体运用:
int main() { int a = 10; // int& b; // 1、引用在定义时必须初始化 //此时b就是a的小名 int& b = a; // 2、一个变量可以有多个引用 int& c = a; int& d = c; // 也可以给别名取别名 // 3、引用一旦引用一个实体,再不能引用其他实体,d不能在引用其他变量。 ++a; int x = 10; b = x; // b是x的别名呢?还是x赋值给b呢?-->是赋值而非别名(调试看地址和原先别名的a是一样的,这和指针不同) return 0; }
- 1)做参数
- ①输出型参数–>指针也能做,但引用相对舒服点。
- ②大对象传参,提高效率–>没有拷贝,引用不开空间。
- 2)做返回值
- ①输出型返回对象,调用者可以修改返回对象。
- ②减少拷贝,提高效率。
做参数时和指针的应用有很大相似性。
引用一般用作输出型参数,
这里的引用和指针作用基本一致,都能找到地址来改变数据。但是tmp为啥不用指针呢?因为a, b都不是地址,而是用的别名,但是也不是地址。
引用用作返回值是有条件的。当函数执行完作用后如果没有被销毁,那他可以用引用,如果被销毁了,就不能用作返回值了。因为当函数被销毁后地址就没了,而引用是一个别名,地址不变,但现在地址都没了还咋用。
#define _CRT_SECURE_NO_WARNINGS 1 #include #include using namespace std; int temp; int func1() { temp = 5; return temp; } int& func2() { temp = 8; return temp; } int main() { int a = 0, b = 0; // 1. 返回函数的普通类型 a = func1(); // 2. 返回函数的引用 b = func2(); // 3. 返回函数的引用去初始化一个新的引用 int& c = func2(); cout << "a = " << a << endl; cout << "b = " << b << endl; cout << "c = " << c << endl; return 0; }
注意:一般传值引用是输入型参数,传址调用可以是输出型参数,也可以做输入型参数,做输入型参数时前面加const,表示只读不改。
2.31.传值返回:生成一个返回对象拷贝作为函数调用返回值。
传值返回跟传值传参的情况很一致,它只是一份临时拷贝,不能修改本质。
2.32传引用返回: 返回对象(n)的别名
传引用返回的正确运用:
这个代码正确吗?为啥结果是对的?
- 假如返回值取得是n的别名(即tmp),而tmp空间上的值就是n原来的数,
- 但是出了Count函数,tmp就被销毁了,之所以还能打印出正确结果是侥幸,如果有的编译器销毁后n被置成随机值,tmp就会取到随机值。
- 越界:好比申请房间,里面存一个变量,理论上除非别人非法入侵,变量是不会被修改的。但你把房间退了,里面还留着你的东西,就无法保障它不丢失,即使你下回去房间还没租给别人,所以东西被你找到了,也只能说是侥幸(因为换一个房间/编译器,是否还能保证找到就不好说了)。
要想使用传引用返回就必须保证:返回值的别名出了作用域而未被销毁,还可以用作返回值。否则,这个别名并未改变它“本名”的地址,当原来的地址被销毁后,再去访问其地址也是胡闹。第一次可能闯上了,但第二次就一定不是了,不信看代码。
printf也属一个函数调用,也需要建立栈帧,
栈帧的大小不一样,第一次可能没有覆盖Count的栈帧,但是第二次可能就覆盖了,所以打印的是随机值。
理解:第一次printf调用后,就被销毁了n,第二次printf再回去找就找失败了。
注意:不要弄混了传值,传址的区别;传值,传引用的区别。
2.33.总结传值返回和传引用返回:
- 出了函数作用域,返回对象就销毁了,那么一定不能用引用返回,一定要用传值返回
- 下面的这个场景(static),出了作用域变量还在,才能使用引用返回
权限只能平移和缩小而不能放大。
大家看这段代码,为啥rdd必须加const修饰呢?
ii是int类型,rdd是double类型,当引用时会发生类型转换,但是ii还是int类型,只不过转换途中会产生一个临时变量。而rdd是这个临时变量的别名。而临时变量具有常性,相当于const修饰过,所以为了权限平移,加const。
权限的放大和缩小只针对引用和指针,对于普通函数,根本没这个概念。
- 对于这个函数,把a拷贝给n,n的改变不会影响a。
但是我们要是用引用传参
b,30根本传不过去,因为n是a的别名,又把b传过去,n改变了,所以a就改变了,所以会报错。
所以如果使用引用传参,函数内如果不改变n,那么建议尽量用const引用传参
- 传引用传参
引用参数则是把形式参数转换成实参,换句话说就是在函数体里面实际操作的是参数的本身,即存放变量值的内存。
- 传值传参
当传入的是普通参数时,实际上函数体做了一个偷天换日的动作,是把普通参数的值,复制到有函数体生成的一个内存中。所以,在函数体中参数的值和传入时的值是一样的,但是它们内存地址是不一样的。
- 传引用返回
返回引用类型和传入引用参数类似,返回的值就是实际中函数体所操作的内存,所以,如果返回类型为引用时,不能将函数体中的局部变量返回(一般返回静态常量)局部变量的生命周期在函数体结束,当函数体结束时,局部变量也跟着结束。返回类型为引用时可以作为左值和右值
- 传值返回
返回普通类型和传入普通类型类似,函数体即将被返回的值也被偷天换日,将准备返回的值放入一个新生成的内存,最后再返回新生成的内存,这样就解决了函数体结束,局部变量也跟着结束的问题。返回类型为普通类型时,只能作为右值(=右边)来使用。
- 引用概念上定义一个变量的别名,指针存储一个变量地址
- 引用在定义时必须初始化,指针没有要求(指针不初始化其值为随机指向)
- 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
- 没有NULL引用,但有NULL指针
- 空指针没有任何指向,删除无害,引用是别名,删除引用就删除真实对象
- 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
- 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
- 有多级指针,但是没有多级引用
- 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
- 引用比指针使用起来相对更安全
- 指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作
函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。
重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。
这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同。
- 例如交换两个数,我们写一个形参是int的,但是传入的值是double型的这个函数就搞不了了,但是C语言不支持同名函数,而C++支持,也就是说把形参的类型转化一下就可以了。
一定要注意函数重载不是函数重写!参数个数,参数顺序,参数类型至少有一个不同才算重载。
仅仅修改函数的返回值是不构成重载的。