003 C++基础篇

前言

大家好,本文将会向您介绍引用(定义、使用场景、引用与值分别作为返回值和参数时的性能比较、引用的权限)

引用

一、引用是什么

引用:定义一个变量的别名,不是新定义一个变量,而是给已经存在的变量取了一个别名,编译器不会为引用变量单独开辟一个内存空间,它和引用的变量共用同一块内存空间

我们使用以下方式定义变量的别名
类型& 引用变量名(对象名) = 引用实体

#include <iostream>
using namespace std;
int main()
{
	int a = 1;
	int& b = a;
	cout << &a << endl;
	cout << &b << endl;
	return 0;
}

我们可以观察到变量a与它引用的变量所属的是同一块空间
在这里插入图片描述
一个变量可以有多个引用(b、c、d)

void Test()
{
       int a = 0;
       int& b = a;
       int& c = a;
       int& d = a;
       cout << &a << endl;
       cout << &b << endl;
       cout << &c << endl;
       cout << &d << endl;
}

在这里插入图片描述

二 、引用的使用场景

1、引用作参数
void swap(int& x1, int& y1)
{
    int tmp = x1;
    x1 = y1;
    y1 = tmp;
}
int main()
{
    int x = 0, y = 1;
    swap(x, y);
    cout << x <<" " << y << endl;
    return 0;
}

根据引用的特性——可知变量x,y与他们的别名是共用同一块空间的,因此对x1,y1的修改可以影响到实参x,y

在这里插入图片描述

2、引用作返回值
//定义一个结构体
struct SeqList
{
       int a[10];
       int size;
       int capacity;
};
//出了函数作用域结构体PS、a[i]都还在
int& SeqListRef(struct SeqList& ps, int i)
{
       assert(i < ps.size);
       return ps.a[i];              //返回数组第i个整型的别名
}
int main()
{
       struct SeqList s;
       s.size = 3;
       //修改返回对象(别名)
      SeqListRef(s, 0) = 10;
      SeqListRef(s, 1) = 20;
      SeqListRef(s, 2) = 30;
       //打印别名
       cout << SeqListRef(s, 0) << endl;
       cout << SeqListRef(s, 1) << endl;
       cout << SeqListRef(s, 2) << endl;
}

在这里插入图片描述
在以上场景中需要注意如果函数返回时,出了作用域,如果返回对象还在,则可以引用返回,如果已经返回给系统后则必须使用传值返回
什么意思呢?我们可以看一个程序

int& Add(int a, int b)
{
       int c = a + b;
       return c;
}
int main()
{
       int& ret = Add(1, 2);  //Add这个函数的返回值的别名为ret
       //Add(3, 4);
       cout << "Add(1,2) is :" << ret << endl;
}

上述程序是引用作返回值的场景,可是得到的结果是随机值
在这里插入图片描述
原因是:返回了一个局部变量的引用,ret引用的是已经销毁的变量c的地址,打印ret的值会导致未定义的行为,表现为打印随机值

关于引用还应注意:
引用在定义时必须初始化
引用在初始化时引用一个实体后,就不能再引用其他实体(即一旦一个实体被赋值给一个引用,那么该引用就只能指向该实体,不能再指向其他实体)
引用与实体的类型必须一致

值和引用作为函数参数的性能比较

struct A
{
    int arr[10000] = {};
};
void TestFunc1(A a)
{
    ;
}
void TestFunc2(A& a)
{
    ;
}
void parameRefAndValue()
{
    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;
    cout << "TestFunc2(A&amp;)-time:" << end2 - begin2 << endl;
}
int main()
{
    parameRefAndValue();
    return 0;
}


值和引用作为返回值时的性能比较

struct A
{
       int arr[10000] = {};
};
A a;
//返回值
A TestFunc1()
{
       return a;
}
//返回引用
A& TestFunc2()
{
       return a; //返回类型为A&
}
void ReturnRefAndValue()
{
       A a;
       size_t begin1 = clock();   //计时开始
       for (size_t i = 0; i < 10000; ++i)
       {
              TestFunc1();
       }
       size_t end1 = clock();     //计时结束
       size_t begin2 = clock();   //计时开始       
       for (size_t i = 0; i < 10000; ++i)
       {
              TestFunc2();
       }
       size_t end2 = clock();     //计时结束
       // 分别计算两个函数运行结束后的时间
       cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
       cout << "TestFunc2(A&amp;)-time:" << end2 - begin2 << endl;
}
int main()
{
       ReturnRefAndValue();
       return 0;
}

在这里插入图片描述
以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常底下的,尤其是当参数或者返回值类型非常大的时,效率就更低

三、引用权限

在引用过程中

权限可以平移

权限可以缩小

权限不能放大
关于权限的相关场景可以看看以下程序

int func()
{
       int a = 0;
       return a;
}
int main()
{
       const int a = 0;
       //1、权限放大
       //error:int& b = a;
       
       //2、非权限放大而是赋值,引用、指针存在权限
       // int b = a;
       
       //3、权限平移
       const int& c = a;
       
       //4、权限缩小
       int x = 0;
       const int& y = x;
       
       //5、当发生类型转换时,会产生一个带有常性的临时变量
       int i = 0; 
       //先将i给临时变量
       //常量是不具有别名的,常量在编译时就被确定了
       //error:double& d = i;
       const double& d = i;
       
       //6.返回一个局部变量
       //出了函数作用域,变量销毁,此时会将值传给一个带有常性的临时变量
       //error:int& ret = func();
       const int& ret = func();
       return 0;
}

小结

本文介绍了引用的相关知识,如果本文存在疏漏或错误的地方,还请您指出,祝您天天开心啦!

posted @ 2023-07-28 11:12  Fan_558  阅读(3)  评论(0编辑  收藏  举报  来源