[C++]C++入门--引用
- 电脑硬件
- 2025-08-21 11:51:01
![[C++]C++入门--引用](/0pic/pp_22.jpg)
🥁作者: 华丞臧 📕专栏:【C++】 博主Gitee 各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞+收藏+关注)。如果有错误的地方,欢迎>在评论区指出。 推荐一款刷题网站 👉LeetCode
文章目录 一、引用概念二、引用特性三、常引用四、使用场景4.1 做参数4.2 做返回值 五、引用和指针的区别
一、引用概念
引用不是新定义一个变量,而是给已经存在的变量取了一个别名,编译器不会为引用变量开辟内存空间,它们引用的变量共用同一块空间。
//类型& 引用变量名 = 引用实体; 如下: int a = 10; int& ra = a; //定义引用类型,必须初始化 二、引用特性 引用在定义时必须初始化;一个变量可以有多个引用;引用一旦引用一个实体,再不能引用其他实体。 void TestRef() { int a = 10; // int& ra; // 该条语句编译时会出错 int& ra = a; int& rra = a; printf("%p %p %p\n", &a, &ra, &rra); }可以看到a,ra,rra三个变量地址一致,从这里可以看出引用其实就是变量实体的别名,与实体共用同一块空间。
三、常引用 void TestConstRef() { const int a = 10; //int& ra = a; // 该语句编译时会出错,a为常量 const int& ra = a; // int& b = 10; // 该语句编译时会出错,b为常量 const int& b = 10; double d = 12.34; //int& rd = d; // 该语句编译时会出错,类型不同; const int& rd = d; }指针和引用赋值中,变量的权限可以缩小,但不可以放大。
类型转换、整型提升、截断这些的过程中都会产生一个临时变量,语法规定临时变量具有常性即不能修改。
四、使用场景 4.1 做参数在C语言当中,调用一个函数传参分为两种:一是传值二是传地址,在这两种方式中只有传地址才能通过函数中的形参改变实参;而现在C++当中使用引用做形参同样能达到相同的效果。
//这是一个例子 void Swap(int& left, int& right) { int temp = left; left = right; right = temp; }引用做参数的优点:
减少拷贝,提高效率;输出型参数,函数中修改形参实参也修改了。关于函数重载:
int Add(int& a, int& b) { int c = a + b; return c; } int Add(int a, int b) { int c = a + b; return c; }上述函数构不构成重载看编译器的识别或者名字修饰规则,但是不能这么写程序会出错,因为调用时存在二义性,编译器识别不出。 传值、传引用效率比较:
#include<iostream> #include <time.h> using namespace std; 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 < 100000; ++i) TestFunc1(a); size_t end1 = clock(); // 以引用作为函数参数 size_t begin2 = clock(); for (size_t i = 0; i < 100000; ++i) TestFunc2(a); size_t end2 = clock(); // 分别计算两个函数运行结束后的时间 cout << "TestFunc1(A)-time:" << end1 - begin1 << endl; cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl; } int main() { TestRefAndValue(); return 0; }可以看到,数据量很大的时候引用做参数的效率更快。
4.2 做返回值传值返回:
我们知道在函数调用时,编译器会为函数在内存中开辟一个函数栈帧;在函数调用返回时函数栈帧销毁之前,返回值会先传给一个临时变量(小则存放在寄存器中,大则在主函数栈帧开辟的空间)返回。
引用返回:
引用是什么?引用是一个变量的别名,和引用的实体是共用一块空间的;那么在函数栈帧销毁时,函数返回的是实体变量在这个过程中就不会再产生一个临时变量。 注意:函数栈帧销毁,该函数中的局部变量也销毁了,这时就不能再用引用返回了;如果这个变量是定义在函数栈帧上的,函数栈帧销毁变量也跟着销毁,出了函数作用域返回变量不存在了,不能使用引用返回,因为此时引用返回的结果是未定义的;出了函数作用域,返回变量存在,才能使用引用返回。
int& Count() { static int n = 0; n++; // ... return n; } //下面代码输出什么结果?为什么? int& Add(int a, int b) { int c = a + b; return c; } int main() { int& ret = Add(1, 2); Add(3, 4); cout << "Add(1, 2) is :"<< ret <<endl; //cout << "Add(1, 2) is :"<< ret <<endl; //再打印一次会是什么结果? return 0; }打印一次: 打印两次: 说明:
打印一次的时候,add函数栈帧销毁时函数栈帧中存放c变量的空间并没有被销毁改变,此时去访问这块空间的值还是7。当第二次打印两次的时候,我们第二次非法访问同样一块空间发现值已经被改变,这是因为先取值再打印所以第一次还是7但是此时cout函数调用开辟了栈帧覆盖了那片空间,从而导致了值的改变。上述的那一段程序是错误,属于非法访问,这说明:出了函数作用域变量就不存在了是不能使用传引用返回的。引用返回的优点:
减少拷贝,提高效率;修改返回值。值和引用的作为返回值类型的性能比较:
#include<iostream> #include <time.h> using namespace std; 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; cout << "TestFunc2 time:" << end2 - begin2 << endl; } int main() { TestReturnByRefOrValue(); return 0; }通过上述代码的比较,发现传值和传引用在作为传参以及返回值类型上效率相差很大。
五、引用和指针的区别在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间;但在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
int main() { int a = 10; int& ra = a; ra = 20; int* pa = &a; *pa = 20; return 0; }上述引用和指针的汇编代码如下: 通过对比不难发现,引用的底层是使用指针实现的。
引用和指针的不同点:
引用概念上定义一个变量的别名,指针存储一个变量地址;引用在定义时必须初始化,指针没有要求;引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体;没有NULL引用,但有NULL指针;在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节);引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小;有多级指针,但是没有多级引用;访问实体方式不同,指针需要显式解引用,引用编译器自己处理;引用比指针使用起来相对更安全。[C++]C++入门--引用由讯客互联电脑硬件栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“[C++]C++入门--引用”
上一篇
4.JVM垃圾收集机制