CPP的引用
引用
- CPP的引用
- :one:引用的概念
- :two:引用的特性
- :three:常引用
- :four:常见的使用场景
- :five: 传值与传引用与指针的比较
- :six:引用和指针的区别
- 一. :arrow_right:**概念:引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。**
- 二. :arrow_right:
- 补充:在取别名的时候对原引用变量,权限只能缩小,不能放大。
- 三. :arrow_right:
- **常引用**![在这里插入图片描述](https://s2.51cto.com/images/blog/202409/02141246_66d5575eb9b911607.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
- 四. :arrow_right:
- **常见的使用场景**
- 注意:如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已**经还给系统了,则必须使用传值返回。**
- 五. :arrow_right:
- 六. :arrow_right:
1️⃣引用的概念
2️⃣引用的特性
- 引用在定义时必须初始化
- 一个变量可以有多给引用
3️⃣常引用
4️⃣常见的使用场景
- 做参数
- 做返回值
5️⃣ 传值与传引用与指针的比较
6️⃣引用和指针的区别
一. ➡️概念:引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
我们使用代码来解释
如图中代码&b的含义是将a别名引用给b,使得b可以获得a开辟空间的值。我们通过图中的调试可以发现它们的地址都是一样并且值是一样的。这说明引用并没有从新开辟空间而是使用a初始化的空间。
二. ➡️
1.引用在定义时必须初始化如果不初始化会出现下面的报错。
正确的初始化:
2.一个变量可以有多给引用
3.引用一旦引用一个实体,再不能引用其它实体
补充:在取别名的时候对原引用变量,权限只能缩小,不能放大。
当权限放大会出现以下报错
const是告诉编译器我只取别名不对其只进行修改就不会出现上面第3点引用一旦引用一个实体,再不能引用其它实体的问题。const可以解决这个问题。
在简单来说const就是只读不写。
三. ➡️
常引用
cpp中的常引用指的是如图中的a,此处a为常量。
我们使用const只取别名就不会出错。
四. ➡️
常见的使用场景
1.做参数
void Swap(int& x, int& y)
{
int tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 0, b = 1;
Swap(a, b);
return 0;
}
在这里面我们使用引用,这边就是为a取别名为x,为b取别名为y。有了这个相比我们之前通过指针调用进行传值的交换要简单的些。
2.做返回值
当我们普通调用的时候你会发现它地址不同,我们换成引用调用再来看。
你会发现此时地址一样
这幅图就解释了普通调用返回参数后临时变量里的n被销毁我们在次调用的时候就变成了随机值。
如果使用引用的话会不会进行销毁。代码图如下
![在这里插入图片描述](
这样子我们可以发现ret的值不变说明c传回去时值就不变。这里面使用了static是为了让c=0这句在第一次调用的时候有用之后进入函数的时候不在执行这句,这句相当就失去效果。这其中可以发现第二次进入的时候c的值从3变成了7。这了间接证明了为什么引用一旦引用一个实体,再不能引用其它实体的原因
注意:如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。
五. ➡️
传值与传引用与指针的比较
我们通过以下代码进行比较
void Swap(int& x, int& y)
{
int tmp = x;
x = y;
y = tmp;
}
void Swap(double& x, double& y)
{
double tmp = x;
x = y;
y = tmp;
}
#include <time.h>
struct A{ int a[10000]; };
void TestFunc1(A aa){}
void TestFunc2(A& aa){}
void TestFunc3(A* paa){}
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();
size_t begin3 = clock();
for (size_t i = 0; i < 10000; ++i)
TestFunc3(&a);
size_t end3 = clock();
// 分别计算两个函数运行结束后的时间
cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
cout << "TestFunc3(A*)-time:" << end3 - begin3 << endl;
}
int main()
{
**加粗样式**int a = 0, b = 1;
Swap(a, b);
double c = 1.1, d = 2.2;
Swap(c, d);
TestRefAndValue();
return 0;
}
这是3者的区别有2个。
区别1:通过这个结果我们可以清晰明白引用和指针作为函数参数会比值做参数编译器运行时间要短。
区别2:引用传参不需要创建临时变量。而值和指针传参需要创建临时变量。引用是直接返回变量的别名。
六. ➡️
:引用和指针的区别
- 引用在定义时必须初始化。
- 引用在初始化时引用了一个实体后,就不能再引用其它实体。
3. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节) - 引用不能为NULL。
- 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
- 访问实体方式不同,指针需要显式解引用,引用编译器自己处理。
- 有多级指针,但是没有多级引用。
- 引用比指针使用起来相对更安全。