当前位置: 首页> 汽车> 新车 > 网页素材网站有哪些_网页模板图片高清_中国十大企业培训机构排名_制作网站免费

网页素材网站有哪些_网页模板图片高清_中国十大企业培训机构排名_制作网站免费

时间:2025/7/12 6:58:43来源:https://blog.csdn.net/m0_68190153/article/details/145911872 浏览次数: 0次
网页素材网站有哪些_网页模板图片高清_中国十大企业培训机构排名_制作网站免费

一、什么是引用?

1、引用的基本用法:

        小伙伴们在学习C语言的时候是不是经常遇到'&'这个符号,在C语言中它具有三种意思,一是取地址操作符,二是二进制按位与,三是当两个'&'放在一起的时候,表示逻辑与。而C++中保存了这些用法,同时也为'&'赋予了一层新的含义——引用(C++11中,'&&'表示右值引用,这个以后再说)

        C++中的引用是一种复合类型,就像指针一样。注意啊,这里埋了一个小伏笔。那引用是什么呢?它其实就是为一个变量取别名,那么通过操作这个引用就可以间接操作变量了,下面看引用的基本用法。

//引用的基础用法
int main()
{int a = 0;int& b = a;//为变量a取别名,这个名字是b,以后可以用b来代替aa = 10;cout << b << endl;//打印结果为:10//a和b就是同一个变量,就是名字不同,所以它们的地址相同cout << &a << endl;//打印结果为:000000525F91FB14cout << &b << endl;//打印结果为:000000525F91FB14return 0;
}

上面的b就是一个引用,将a赋给b时,a就是b,b就是a,它们也指向同一块空间。

2、引用和指针:

//引用和指针
int main()
{int a = 0;int& b = a;int* c = &a;cout << &a << endl;//打印结果为:000000F079DFFAF4cout << &b << endl;//打印结果为:000000F079DFFAF4cout << &(*c) << endl;//打印结果为:000000F079DFFAF4return 0;
}

        在上面程序的输出结果可以看出,a、b、c三者的地址相同,这是不是给了我们一种思想,就是引用可以替代指针呢?

        还记得以前的Swap()函数吗,如果是值传递就不会真正的交换,需要地址传递才行,形参部分使用指针接收,那如果引用可以替代指针,我们如果使用引用接收会怎样呢?下面请看代码。

//使用引用接收实参
void Swap(int& x, int& y)
{int tmp = 0;tmp = x, x = y, y = tmp;
}int main()
{int a = 10, b = 20;Swap(a, b);printf("a = %d, b = %d\n", a, b);//打印结果为:a = 20, b = 10return 0;
}

        上面代码的结果中,居然真的把a和b中的内容交换了,说明引用确实可以替代指针(注意,这时候还没有探索出引用和指针的区别——又埋了一个伏笔)。另外形参的x和y就是实参的a和b的别名,它们同属于一块空间,所以可以在函数内部实现交换。

二、引用有什么用?

1、引用的特性:

引用有一下几点需要注意,先写出来,之后逐一分析。

1)引用不能声明,并且定义时必须初始化。

2)引用初始化时候不能像指针一样,初始化成“空引用”。

3)引用一旦引用了一个实体(或者称为对象或变量)时,就不能再引用其他实体了。

4)引用可以做函数的返回值,但是在某些情况下是非常危险的。

5)如果一个引用不会被修改,或者不希望其修改,尽量使用count修饰常引用)。

6)当需要使用指针接收实参时(比如上面的Swap()函数),可以写成引用,这样便于理解(没有学过引用的同学就不要这样做了)

//引用的特性
int main()
{//引用必须在定义时初始化//int& b;//非法的引用//一个变量可以有多个引用int a = 0;int& b = a, & c = a;//a的别名分别是b和ccout << "b = " << b << " c= " << c << endl;//打印结果为:b = 0 c= 0//引用一旦引用一个实体,就不能引用其他实体int d = 0;b = d;//这么写就会产生歧义,要么是把d的值赋给b,要么是把b变成d别名//为了解决这个问题,所以引用就不能再引用其他实体return 0;
}

2、引用做返回值:

//引用做返回值
//传值返回,返回的是n的值,使用ret接收,除了函数之后n被销毁
int Count()
{int n = 0;n++;return n;
}//传引用返回,返回的是n的别名,出了函数之后可以通过ret访问到n,但n已经被销毁
int& Count(int x)
{int n = 0;n++;return n;
}int main()
{int ret = Count();//接收到的是n的值ret = Count(10);//接收到的是n的别名,通过操作ret可以改变n//这样的话就会造成越界访问,这是非常危险的cout << ret << endl;//这个打印结果可能是1,也可能是随机值int& ret1 = Count(10);cout << ret1 << endl;//这个结果是1或者随机值cout << ret1 << endl;//这个结果就是随机值(32位平台),64位平台仍然是1return 0;
}

        现在提出几个问题,为什么上面的ret输出结果是1或随机值?为什么ret1第二次输出的结果在32位平台上是随机值,在64位平台上还是1(或者说1也是随机值)?

        首先ret的输出和ret1的第一次输出结果是一样的,都有可能是1或随机值,这是由编译器和操作系统决定的。ret2的第二次输出结果在32位平台上是随机值,原因是调用Count()函数之后,为变量n开辟了一块空间,当第二次输出ret的时候,n是局部变量被销毁,它的空间也被覆盖(这时候cout建立的栈帧覆盖了Count()函数建立的栈帧,那么n的空间被占据了,值自然也成为随机值了),所以结果在32位平台上是随机值。

下面看这个代码。

//引用做返回值
int& Count()
{int n = 0;n++;return n;
}int main()
{int ret = Count();cout << ret << endl;cout << ret << endl;return 0;
}

        为什么ret不作为n的别名(即定义ret时没有加上引用),同样在32位平台上,结果就不是随机值呢?因为ret此时对n来说相当是值拷贝(在第一次输出的时候,ret内部的值就可能是1或随机值了),虽然函数返回的是n的别名,但使用的是变量ret接收,并不是n的别名接收。所以不管打印几次,结果都是输出n的值。

        总的来说,int ret = Count()是将n的值传给ret,ret中一直保存着这个值,打印几次都不变。int& ret = Count()是将n的别名返回,使用一个别名ret接收,第一次输出通过ret找到n所指向的空间(虽然这块空间已经不属于n了,但是没有消失,内部的值也是随机的),第二次输出仍然是找那块空间,但是被cout()函数创建的栈帧覆盖了,那块空间里面的值自然就是随机值了。

从上面的代码中还可以看出,返回别名的函数,接收时不是必须使用别名接收。

下面我们看第二次输出仍然不是随机值的情况。

//引用做返回值
int& Count(int x)
{int num[1000] = { 0 };int n = 0;n++;return n;
}int main()
{int& ret = Count(10);cout << ret << endl;cout << ret << endl;return 0;
}

        同样是在32位平台上输出的,也使用了引用,为什么两次结果都是1呢?唯一改动的就是Count()函数内部多开辟了一块空间,这块空间很大,所以栈帧分配的时候变量n就被分到了num的下面(从num开始分配空间)。当第二次输出的时候,cout()函数的栈帧没有Count()函数的栈帧大,所以变量n的空间没有被覆盖,所以还是1或随机值。

        函数返回时,出了函数作用域,如果返回对象还未被销毁,就可以使用引用返回,如果已经被销毁了,则必须使用传值返回,否则是非常危险的。

3、引用的效率:

        现在我们知道了引用可以代替指针,都知道函数传参时,传递地址使用指针接收的效率要比传递值的效率高,那我们现在对比一下传引用和传址的效率。

//传值传参和传引用传参的效率对比
#include <time.h>
struct A{ int a[10000]; };
void TestFunc1(A a) {}//传址传参
void TestFunc2(A& a) {}//传引用传参//比较效率
void TestRefAndValue()
{A a;//以值作为函数参数size_t begin1 = clock();for (size_t i = 0; i < 10000; ++i)TestFunc1(a);size_t end1 = clock();//以引用作为函数参数size_t begin2 = clock();for (size_t i = 0; i < 10000; ++i)TestFunc2(a);size_t end2 = clock();//分别计算两个函数运行结束后的时间cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;//打印结果为:TestFunc1(A)-time:7cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;//打印结果为:TestFunc2(A&)-time:0
}int main()
{TestRefAndValue();//从上面输出的结果可以看出,传值传参和传引用传参相差7毫秒//所以传引用传参的效率要比传值传参高return 0;
}

        这也应证了那句话,就是传址传参用指针接收的效率要高于传值传参,而目前我们所知道的是引用可以替代指针,所以传引用的效率高于传值

//值和引用作为返回值类型的性能比较
#include <time.h>
struct A{ int a[10000]; };A a;//定义结构体变量
//之所以定义全局结构体变量,是因为引用返回要保证变量不被销毁A TestFunc1() { return a; }//值返回
A& TestFunc2() { return a; }//引用返回//比较效率
void TestReturnByRefOrValue()
{//以值作为函数的返回值类型size_t begin1 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc1();size_t end1 = clock();//以引用作为函数的返回值类型size_t begin2 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc2();size_t end2 = clock();//计算两个函数运算完成之后的时间cout << "TestFunc1 time:" << end1 - begin1 << endl;//打印结果为:TestFunc1 time:139cout << "TestFunc2 time:" << end2 - begin2 << endl;//打印结果为:TestFunc2 time:1
}int  main()
{TestReturnByRefOrValue();//从结果可以看出,引用返回比值返回的效率高很多return 0;
}

传引用传参和传引用返回:

传引用传参的特点是提高效率,在任何时候都可以使用,对于输出型参数,可以通过修改形参从而影响实参。

传引用返回的特点是提高效率,但是在出了函数作用域对象还在时才可以使用引用返回,引用返回还可以修改返回对象。

        所以引用存在的的意义有三点,一是可以作为输出型参数(代替指针,容易理解),二是可以提高效率,三是可以引用返回,只要空间不销毁,就可以对空间进行修改

4、常引用(用const修饰引用):

        在学习常引用时,引入了权限的概念。那什么是权限呢?比如一个变量,它可以被修改,这时正常的权限。当这个变量被const修饰之后,就是失去了可以被修改的这个特性,那么权限也就缩小了。那如果这个被const修饰的常量,去掉const之后又可以被修改了,这种情况就是权限放大了。同时权限还可以平移,也就是可修改的变量还可修改,不可修改的仍不可修改。

//权限
int main()
{const int a = 10;//int& b = a;//当a被const修饰之后,就不能进行修改了//所以这行语句是错误的,因为a的权限被b放大了,而权限可以缩小和平移,但不能放大int b = 10;const int& c = b;//这行代码可以,因为c将b的权限缩小了int& d = b;//这个是权限的平移,所以也可以return 0;
}
//常引用
int func()
{int a = 10;return a;
}int main()
{int i = 0;const int& j = i;//权限缩小可以double x = i;//赋值可以//double& y = i;//i是int型的,y是double的//将i强转成y时会产生临时变量,而临时变量具有常属性,所以这样相当于权限放大const double z = i;//权限平移//int& ret = func();//因为func()返回a的拷贝,是临时对象,具有常属性const int& ret = func();//这样属于权限平移return 0;
}

三、引用的本质:

        现在终于可以探寻引用的本质了,之前一直都是一种“引用可以代替指针的态度”,并且它的效率和指针一样,都要比值传参值返回要高,那引用的本质到底是什么呢?(埋了两个伏笔,终于要揭晓了)先看一段代码。

//引用的本质
int main()
{int a = 10;int& b = a;int* c = &a;a = b++;a = (*c)++;return 0;
}

        这这这这这,搞了这么半天,这引用不就是指针嘛,它们的底层完全相同啊,那知道了指针是什么,该如何使用,不就知道啥是引用了?说得对,但也不完全对,正所谓世界上没有完全相同的两片树叶,那也就没有完全相同的两个C++语法。引用的底层确实是使用指针实现的,好比真假美猴王一样,不然怎么和指针那么想呢,可是又不完全是指针,原因有三:

1)指针可以声明,定义时也可以不初始化(虽然这样做不太好),引用必须在定义时候初始化

2)指针可以指向空(即NULL),但引用不能为空,没有“空引用”之说。

3)指针如果搞不好会成为野指针,但同样没有“野引用”之说,正如上面的代码,只是引用了一块释放的空间,访问的话会出现随机值(有时候也会报错或者程序崩溃)。

所以使用引用的注意事项,小伙伴们都记住了吗。

关键字:网页素材网站有哪些_网页模板图片高清_中国十大企业培训机构排名_制作网站免费

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: