• C++引用专题总结


    1 变量名的回顾

    在C和C++中,变量名的实质是什么了?

    变量名的实质是一段连续存储空间的别名,是一个标号(名牌号)如下图所示,程序中通过变量来申请并且命名内存空间,通过变量的名字可以使用存储空间。但是这可以提出一个问题。对一串连续的内存空间只能取一个别名吗?这就引出了C++中引用的概念。

    image

    2 引用的概念

    • 在C++中新增加了引用的概念。
    • 引用可以看做一个已定义变量的别名。
    • 引用的语法: Type& name = var;
    void main()
    {
    	int  a = 10; //c编译器分配4个字节内存 a内存空间的别名
    	int &b = a;  //b就是a的别名
    	a = 11;      //直接赋值
    	{
    		int *p = &a;
    		*p = 12;
    		printf("a %d 
    ",a);
    	}
    	b = 14; 
    	printf("a:%d b:%d", a, b);
    	system("pause");
    }
    // 对a和b的操作其实是一样
    

    这里必须注意的是,引用是C++编译器对于C的扩展,在C编译的环境中是无法使用的。

    3 引用做函数参数

    • 普通引用在声明时必须用其他的变量对其进行初始化。
    • 引用作为函数参数声明时不进行初始化。

    对于复杂类型的引用其实也和普通的变量是一致的。举个栗子:

    //复杂数据类型的引用
    struct Teacher
    {
    	char name[64];
    	int age ;
    };
    
    void printfT(Teacher *pT)
    {
    	cout<<pT->age<<endl;
    }
    
    //pT是t1的别名 ,相当于修改了t1
    void printfT2(Teacher &pT)
    {
    	//cout<<pT.age<<endl;
    	pT.age = 33;
    }
    
    //pT和t1的是两个不同的变量
    void printfT3(Teacher pT)
    {
    	cout<<pT.age<<endl;
    	pT.age = 45; //只会修改pT变量 ,不会修改t1变量
    }
    void main()
    {
    	Teacher t1;
    	t1.age = 35;
    
    	printfT(&t1);
    
    	printfT2(t1); //pT是t1的别名
    	printf("t1.age:%d 
    ", t1.age); //33
    
    	printfT3(t1) ;// pT是形参 ,t1 copy一份数据 给pT     //---> pT = t1
    	printf("t1.age:%d 
    ", t1.age); //35
    	
    	cout<<"hello..."<<endl;    
    	system("pause");
    	return ;
    }
    
    

    那么有人就会疑惑,这种操作我在C中就有指针代替,为什么要有引用这种概念了?

    4 引用的意义

    • 引用作为其他变量的别名,作为这一点来说,它在一些场合可以代替指针
    • 引用相对于指针而言具有更好的可读性和实用性

    我们可以来比较两个代码,看看指针和引用哪一种方式更为直观和便于阅读。

    int swap(int &a, int &b)
    {
    	int temp = a;
    	a = b;
    	b = temp;
    	return 0;
    }
    
    int swap(int *a, int *b)
    {
    	int temp = *a;
    	*a = *b;
    	*b = temp;
    	return 0;
    }
    

    想必大家都有自己的答案吧。

    5 引用本质的思考

    我们说引用是变量的别名,这个是很直接可以观察和测试,但是C++编译器这个bitch背着我们在背后做了什么勾当了?

    image

    • 引用在C++内部的实现是一个常指针,Type &name === Type *const name
    • C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同。
    • 从使用的角度,引用会让人误会其只是一个别名,没有自己的存储空间。这是C++为了实用性而做出的细节隐藏
    • 当我们使用引用语法的时,我们不去关心编译器引用是怎么做的,当我们分析奇怪的语法现象的时,我们才去考虑c++编译器是怎么做的,从实际出发。

    6 函数的返回值是引用

    当函数的返回值是引用时,这个是引用的一个难点问题。

    • 当函数的返回值为栈变量时,不能成为其他引用的初始化值,不能成为左值。
    • 若返回是静态变量或者全局变量,可以成为其他引用的初始值,也可以成为左值使用,也可以作为右值使用。

    举个栗子

    int getValue1()
    {
    	int a;
    	a = 10;
    	return a;
    }
    
    //基础类型a返回的时候,也会有一个副本
    int& getValue2()
    {
    	int a;
    	a = 10;
    	return a;
    }
    
    int main()
    {	
    	int a1 = getValue1();
    	int a2 = getValue2();
    	int &a3 = getValue2();
    	//这三个里面那几个是正常的
    	printf("a1: %d, a2: %d, a3: %d",a1, a2, a3);
    	return 0;
    }
    
    

    这道我觉的很有参考意义,首先int a1 = getValue1();是很正常的方法,肯定没有什么问题,然后int a2 = getValue2();因为getValue2()的返回值是一个引用,所以你用什么方式来接这个引用编译的做法就不一样。

    如果你使用变量来接,如int a2 = getValue2();这个编译就直接把引用的值取出来,作为副本直接赋值给a2,如果你使用引用初始化的方式来接返回值,如 int &a3 = getValue2();这个时候编译就返回引用的本质,引用的本质之前讨论了是一个常指针,所有a3就是一个地址,这个时候getValue2()结束,局部变量解析,但是由于编译不同和编译方式的不同,其局部变量可能被覆盖也可能保持不变。这个时候使用printf函数,C++编译器判断a3是一个引用,就偷偷在a3前加一个*这样就取出a3地址的值就是10.

    结果如下:

    image-20211022222147523

    7 常引用

    • 常引用是变量的引用只有只读属性,不能通过引用去修改变量。

    • 常引用的初始化分为两种情况

      • 第一种是用变量去初始常引用,int x = 10; const int & y = x;

      • 第二种是用字面量初始常量引用

        int &m = 41; //这种方式是错误,字面量是没有地址,引用是内存地址的别名,没有地址没法取的。

        const int&m = 41; // 在前面加入const 变成常引用就可以通过编译,本质还是编译那个bitch会偷偷给字面量分配空间

    7.1结论

    C语言中的const变量:

    • C语言中的const变量是只读变量,有自己的存储空间。

    C++中的const变量:可能分配空间也有可能不分配空间。

    • const为全局变量时,并且在其他的文件中需要使用,会分配内存空间
    • 当使用&操作符,取const常量地址时,也分配内存空间。
    • const int &a = 10; const修饰引用,也会分配存储空间
  • 相关阅读:
    JS加密库
    异常处理
    uva 10673 Play with Floor and Ceil
    执⾏ Python 程序的三种⽅式----pycharm安装
    第⼀个 Python 程序
    认识 Python
    svg的使用
    elementUI中el-image显示不出来图片?img与el-image的区别
    类型转化与变量
    liunx
  • 原文地址:https://www.cnblogs.com/Kroner/p/15440978.html
Copyright © 2020-2023  润新知