喵喵喵?喵嘞个咪?---- C++左值引用

编译环境:VS 2013



一、C 语言传参
1. 传值
void swap(int left, int right)
{
	int temp = left;
	left = right;
	right = temp;
}

这个函数很简单,就是一个交换两数的函数,我们编译运行:
在这里插入图片描述
咦?我明明通过函数交换了啊,怎么值还是没变呢?
原来是因为,仅仅在这个函数内部完成了两数的交换,调用者本身并没有发生交换,我们可以调用内存查看
在这里插入图片描述
通过监视和内存,我们发现,left 和 right 的值确实交换了
swap 函数执行完毕,我们再来看看 main 函数的 a,b
在这里插入图片描述
我们看到了,main 函数内的 a,b 的值并未交换,细心的我们发现,left 的地址和 a 的地址不同,right 的地址和 b 的地址不同,为什么呢?
因为:

  • C 语言,在函数调用过程中,会生成一份临时变量,最终把实参的值传给临时变量,即形参是实参的一份临时拷贝,修改形参的值并不会改变实参的值
    这样做,避免了函数调用的副作用,但也使得无法在函数内部改变实参的值
  • 如果想通过形参改变实参的值,只能通过指针传递
2. 传址
void swap(int *left, int *right)
{
	int temp = *left;
	*left = *right;
	*right = temp;
}

我们编译运行之:
在这里插入图片描述
果然,两数交换了!
我们再调试,调用监视查看一下:
在这里插入图片描述
我们看到,left 接收的是 a 的地址,解引用就是变量 a,同样 right 也一样,这样就达到了交换两数的目的。
指针确实可以解决问题
在 C++ 中我们采用另一种方式,也可以解决这个问题,那就是左值引用

二、引用
1. 引用的概念

引用(reference) 是为对象起了另外一个名字,不是新定义一个变量,而是给已存在变量取了一个别名
怎样证明我们没有定义一个新变量呢?
我们知道,定义变量就要开辟内存,如果没有开辟内存,那就没有定义新变量,我们调试查看一下就知道了:

#include <iostream>
#include <stdlib.h>

int main(void)
{
	int a = 10;
	int &refa = a;
	std::cout << a << std::endl;
	std::cout << refa << std::endl;

	system("pause");
	return 0;
}

左值引用
我们看到,变量 a 和 refa 的地址相同,确实没有开辟新内存!!!
编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间
类型& 引用变量名(对象名) = 引用实体;

这里的类型必须和引用实体是同种类型的。

2. 引用的特性
  • 引用在定义时,必须初始化
  • 引用一旦引用一个实体(对象),就不能再引用其他实体了
    引用相当于绑定,和初始值绑定在一起
  • 一个变量可以有多个引用
  • 不能定义引用的引用
    因为引用本身不是一个对象
3. const 的引用

在说 const 的引用之前,我们先来区别一下 C 语言和 C++ 的 const 限定符的不同。
我们说如果不希望某个变量的值被修改,可以加 const 限定符限定。
在 C 语言中:

  • const 修饰的常变量不可以定义数组
    C 中常变量和变量的唯一区别是常变量不能做左值
    左值:既可以出现在赋值符(等号)左边作为可以被修改的变量(或表达式),也可以出现在等号右边的变量(或表达式)
    右值:只能出现在等号右边的变量(或表达式)
  • 在 C 中可以通过指针修改 const 常变量:
    int b = 20;
    const int a = 10;
    int *p = &a;
    *p = 20;
  • const 修饰指针
    const int * p; // p 所指向的变量不能被修改,p可以修改
    int const * p; // p 所指向的变量不能被修改,p可以修改
    int * const p; // p 所指向的变量可以被修改,p 不能被修改指向
    const int * const p; // p 自身和所指向的变量都不能被修改
    const 修饰的是离自己最近的变量

在 C++ 中

  • 常变量可以定义数组,可以作为数组下标。
  • const 修饰一个变量,这个变量就是一个常量,不能被修改,指针也不行
    const int * p; // p 所指向的变量不能被修改,p可以修改
    int const * p; // p 所指向的变量不能被修改,p可以修改
    int * const p; // p 只能指向一个可以被修改变量,但 p 不能修改指向
    const int * const p; // p 自身和所指向的变量都不能被修改
    这一点和 C 语言貌似一样

const 的引用

  • 可以把引用绑定到 const 对象上,常量的引用
    const int a = 1024;
    const int &refa = a;
    这里的 refa 是 a 的一个引用,同样 refa 不能被修改
  • 不能让一个非常量引用指向一个常量对象
    这也再次说明引用的类型必须与其所引用对象的类型一致
    但有两个例外
  1. 在初始化常量引用时允许用任意表达式作为初始值,只要该表达式的结构能转换成引用的类型即可。
    int i = 10;
    const int &r1 = i; // 允许将 const int& 绑定到普通 int 对象上
    const int &r2 = 10; // r2 是一个常量引用
    const int &r3 = r1 * 2; // r3 是一个常量引用
    我们再做个试验:
    double dval = 3.14;
    const int ri = dval;
    std::cout << ri << std::endl;
    这段代码的执行结果是 3 ,数据丢失了
  • 允许一个常量引用绑定非常量对象、字面值,甚至一般表达式
4. 数组引用
int a[5] = { 0, 1, 2, 3, 4 };
int(&b)[5] = a;

数组是可以引用的
但是
int &b[5] = a;
这种写法是不对的!!!!!!
编译器报错是:

error C2440: “初始化”: 无法从“int [5]”转换为“int *[5]”

5. 引用的使用场景
  • 作为函数形参
#include <iostream>
#include <stdlib.h>

void swap(int &left, int &right)
{
	int temp = left;
	left = right;
	right = temp;
}

int main(void)
{
	int a = 10;
	int b = 20;
	std::cout << "a = " << a << " " << "b = " << b << std::endl;
	swap(a, b);
	std::cout << "a = " << a << " " << "b = " << b << std::endl;
	
	system("pause");
	return 0;
}

编译执行:

果然达到了交换的目的

  • 作为函数返回值
#include <iostream>
#include <stdlib.h>

int& TestRefReturn(int &a)
{
	a += 10;
	return a;
}

int main(void)
{
	int a = 10;
	std::cout << "a = " << a << std::endl;
	TestRefReturn(a);
	std::cout << "a = " << a << std::endl;

	system("pause");
	return 0;
}

编译执行:
在这里插入图片描述
我们再来看看下面这段代码输出什么:

int& Add(int a, int b)
{
	int c = a + b;
	return c;
}

int main(void)
{
	int &ret = Add(10, 20);
	Add(100, 200);
	std::cout << "ret = " << ret << std::endl;

	system("pause");
	return 0;
}

30?我们编译运行:
在这里插入图片描述
300?出乎意料!!!
为什么呢?
因为我们返回了栈空间上的引用!!!

6. 引用和指针的区别

其实我们发现,这个引用和指针貌似很像,接下来,我们来看看他们有哪些相同点,又有那些不同点:

  • 相同点
    底层实现方式相同,都是按照指针的方式实现的
    在这里插入图片描述

  • 不同点

引用指针
在定义时必须初始化没有要求
初始化之后,不能再指向其他对象可以在任何时候指向任何一个同类型对象
没有 NULL 引用有 NULL 指针
sizeof 求得的大小为引用类型的大小32位系统 4 个字节
引用自加改变变量内容指针自加改变了指针的指向
没有引用的引用但有指针的指针
编译器寻址手动寻址

引用比指针相对安全

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值