C++ 引用

devtools/2024/11/15 0:45:18/

C++新增了一种复合类型--引用。引用是已定义的变量的别名。例如将data作为value的引用(别名),则可以随意使用data或value来表示该变量。
引用有何作用呢? 其主要作用是作为函数的形参(和函数的返回值)。这样函数的形参就是实参本身,而不是其副本。这样除指针外,引用也为函数处理大型结构提供了一种非常方便的途径,同时对于设计类来说,引用也是必不可少的。

1.创建引用变量

类型名 & 引用变量名 = 被引用的变量名;
int main()
{int a = 100;int& b = a;//b是a的引用cout << "a=" << a << ",b=" << b << endl;++a;cout << "a=" << a << ",b=" << b << endl;++b;cout << "a=" << a << ",b=" << b << endl;//输出两个变量的地址cout << "&a=" << &a << ",&b=" << &b << endl;return 0;
}

3.png

3.png

int a = 10;
cout<<&a<<endl;//&表示取地址符
int &b = a; //&表示引用

这两个怎么区分呢?在定义语句(前面有数据类型)中&表示引用;在使用语句(前面没有数据类型)中&表示取地址符。


//&:取地址符和引用的区别
int main()
{int a = 10;int b = 20;int* p = &a;//&取地址符, a的前面没有类型int& b = a;// &引用 ,b的前面有类型int* q = p;//指针赋值int* & p1 = p;//&引用,p1是p的引用int** q2 = &p;//&取地址符,p的前面没有类型return 0;
}

第9行int *& p1 = p;比较复杂,因为p的类型是int *,所以它的引用应该是int * &。
对于C语言用户而言,首次接触到引用时可能也会有些困惑,因为你很自然地会想到指针,但它们之间还是有区别的。例如,可以创建指向a的引用和指针:

int a = 10;
int &b = a;//引用,b是a的别名
int *p = &a;//指针,p保存a的地址

这样,表达式b和*p都可以表示a,而表达式&b和p都可以表示&a。从这一点来说,引用看上去很像伪装的指针(其中,*解引用运算符被隐藏)。实际上,引用除了和指针表示法不同外,还有其它的区别。
指针和引用区别:1.引用在定义时必须将其初始化,而指针可以先定义,再赋值。

int c = 10;
int &d;//错误,引用在定义时必须初始化
d = c;
int *p;//合法,定义指针不初始化
p = &c;//合法,给指针赋值

注意: 引用在定义时必须进行初始化
指针和引用区别:2.引用更接近const指针,一旦与某个变量关联起来,就将一直效忠于它,不能再作为别的变量的引用。也就是说:

int &b = a;

实际上和下述代码的效果类似:

int *const p = &a;

其中,引用b扮演的角色和*p相同。
下面的代码,试图将a变量的引用,改为b变量的引用,将发生什么呢?


int main()
{int a = 10;int b = 20;int& c = a;  //c是a的引用cout << "a=" << a << ",c=" << c << endl; //输出a,c的值cout << "&a=" << &a << ",&c=" << &c << endl;//输出a,c的地址cout << endl;c = b; //试图将c修改为b的引用,c有没有引用b呢?cout << "b=" << b << ",c=" << c << endl;//输出b,c的值cout << "&b=" << &b << ",&c=" << &c << endl;//输出b,c的地址cout << endl;c = 50;  //修改引用的值cout << "a=" << a << ",b="<<b<<",c=" << c << endl;//输出a,b,c的值cout << "&a="<<&a<<",&b=" << &b << ",&c=" << &c << endl;//输出a,b,c的地址return 0;
}

执行结果如下:

3.png

2.将引用作为函数参数

引用经常被用作函数参数,使得函数中的形参为调用程序中实参的别名。这种传递参数的方法称为按引用传递。C++新增这项特性是对C语言的超越,C语言只能按值传递或按指针传递。按值传递导致形参使用的仅仅是实参的副本。
下面通过交换函数来进行演示:

//按值传递,形参只是实参的副本,不能达到交换值的效果
void Swapv(int x, int y)//错误的
{int tmp = x;x = y;y = tmp;
}//按指针传递,可以实现两个数的交换
void Swapp(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}//按引用传递
void Swapr(int& x, int& y)
{int tmp = x;x = y;y = tmp;
}int main()
{int a = 10, b = 20;//创建三组需要交换的变量int c = 10, d = 20;int e = 10, f = 20;cout << "按值传递,交换前:" << a << "," << b << endl;Swapv(a,b);cout << "按值传递,交换后:" << a << "," << b << endl;cout << endl;//输出空行cout << "按指针传递,交换前:" << c << "," << d << endl;Swapp(&c, &d);cout << "按指针传递,交换后:" << c << "," << d << endl;cout << endl;//输出空行cout << "按引用传递,交换前:" << e << "," << f << endl;Swapr(e, f);cout << "按引用传递,交换后:" << e << "," << f<< endl;return 0;
}

3.png


数据传递示意图如下:

3.png

比较函数Swapr(按引用传递)和Swapp()(按指针传递)。第一个区别是声明函数参数的方式不同:

void Swapr(int &x,int &y);
void Swapp(int *p1,int *p2);

另一个区别是指针版本需要在函数中全程使用*p1和*p2.而引用版本只需要使用x,y即可。

3.const引用

用const修饰的引用称为const引用,也称为常引用。
下面的程序用来计算x的立方,为了说明引用参数的特性,下面的代码写得有点奇怪。


//按值计算立方
int Cubev(int x)
{x = x * x * x;return x;
}//按引用计算立方
int Cuber(int& x)
{x = x * x * x;return x;
}int main()
{int a = 2;int b = 3;int a3 = Cubev(a);int b3 = Cuber(b);cout << a << "的立方是" << a3 << endl ;cout << b << "的立方是" << b3 << endl;return 0;
}

3.png

int Cuber(const int &x);

如果这样做,当编译器发现代码修改了x的值时,将生成错误信息。

3.png

注意:在引用传递中,由于形参就是实参的别名,对于形参的修改能直接反应到实参,如果这个函数不修改参数的值,则应该加上const。

普通的引用在使用时有一些注意事项,如下:

//普通的引用
int main()
{int a = 10;//普通变量const int b = 20;//const常量float c = 12.5f;int & a1 = a;//合法//int& b1 = b;//非法,不能从const int转为int&//int& c1 = c;//非法,不能从float转为int &//int& d1 = 100;//非法,不能引常量//int& e1 = a + 10;//非法,a+10是个临时变量return 0;
}

const引用不仅可以引用普通变量,还可以引用const变量,常量和表达式。如下:


//const引用
int main()
{int a = 10;//普通变量const int b = 20;//const常量float c = 12.5f;int & a1 = a;//合法const int& b1 = b;//合法const int& c1 = c;//合法,会丢失小数const int& d1 = 100;//合法const int& e1 = a + 10;//合法cout << a1 << "," << b1 << "," << c1 << "," << d1 << "," << e1 << endl;return 0;
}

3.png

const引用作为形参

如下面的示例,const引用作为形参,则实参的种类可以非常的丰富。

//按引用计算立方
int Cuber(const int& x)
{return x*x*x;
}int main()
{int a = 2;int& b = a;long c = 5;int* p = &a;int arr[4] = {1,2,3,4};int a3 = Cuber(a);int b3 = Cuber(b);int c3 = Cuber(c);//产生临时变量int d3 = Cuber(*p);int e3 = Cuber(arr[2]);int f3 = Cuber(4);//产生临时变量int g3 = Cuber(a+2);//产生临时变量cout << a << "的立方是" << a3 << endl;cout << b << "的立方是" << b3 << endl;cout << c << "的立方是" << c3 << endl;cout << *p << "的立方是" << d3 << endl;cout << arr[2] << "的立方是" << e3 << endl;cout << 4 << "的立方是" << f3 << endl;cout << a+2 << "的立方是" << g3 << endl;return 0;
}

3.png

应尽可能的使用const

将引用参数声明为const的理由有三个:

●使用const可以避免无意中修改数据;
●使用const能够处理const和非const实参,否则只能接受非const实参;
●使用const引用使函数能够使用临时变量。

4.指针与引用有什么区别?

请看下面链接的博客:

http://t.csdnimg.cn/5Pudv


http://www.ppmy.cn/devtools/5268.html

相关文章

appium2报错:Failed to create session. ‘automationName‘ can‘t be blank

1、问题概述&#xff1f; 今天在window环境中安装了appium2.5.2版本&#xff0c;通过appium inspector连接真机的时候报错如下&#xff1a; Failed to create session. automationName cant be blank 原因分析&#xff1a;这是因为appium2的比appium1有了很大的改进&#xff…

算法学习——LeetCode力扣补充篇8(146. LRU 缓存、 215. 数组中的第K个最大元素、25. K 个一组翻转链表)

算法学习——LeetCode力扣补充篇8 146. LRU 缓存 146. LRU 缓存 - 力扣&#xff08;LeetCode&#xff09; 描述 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化…

游戏前摇后摇Q闪E闪QE闪QA等操作

备注&#xff1a;未经博主允许禁止转载 个人笔记&#xff08;整理不易&#xff0c;有帮助&#xff0c;收藏点赞评论&#xff0c;爱你们&#xff01;&#xff01;&#xff01;你的支持是我写作的动力&#xff09; 笔记目录&#xff1a;学习笔记目录_pytest和unittest、airtest_w…

kaggle 房价预测 得分0.53492

流程 导入需要的包引入文件,查看内容数据处理调用模型准备训练输出结果 导入需要的包 import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split from sklearn.linear_model i…

postman几种常见的请求方式

1、get请求直接拼URL形式 对于http接口&#xff0c;有get和post两种请求方式&#xff0c;当接口说明中未明确post中入参必须是json串时&#xff0c;均可用url方式请求 参数既可以写到URL中&#xff0c;也可写到参数列表中&#xff0c;都一样&#xff0c;请求时候都是拼URL 2&am…

HarmonyOS开发实例:【分布式新闻客户端】

介绍 本篇Codelab基于栅格布局、设备管理和多端协同&#xff0c;实现一次开发&#xff0c;多端部署的分布式新闻客户端页面。主要包含以下功能&#xff1a; 展示新闻列表以及左右滑动切换新闻Tab。点击新闻展示新闻详情页。点击新闻详情页底部的分享按钮&#xff0c;发现周边…

Python学习笔记1:变量命名

跟学极客时间的教程系列笔记&#xff1a; 1&#xff09; Python 还支持更灵活的动态解包语法。只要用星号表达式 &#xff08;*variables&#xff09;作为变量名&#xff0c;它便会贪婪 地捕获多个值对象&#xff0c;并将捕获到的内容作为 列表赋值给 variables。比如&#…

Android系统学习 —— 替换crash_dump文件

步骤 查看crash_dump所在的路径 vsoc_x86_64:/ # which crash_dump64 /apex/com.android.runtime/bin/crash_dump64可以看到&#xff0c;crash_dump放在了/apex/com.android.runtime下面。但是这个目录下的文件即使remount了也无法修改&#xff0c;而是需要通过/system/apex来…