C++之引用

一、引用语法

1.引用基本作用

引用可以看成是数据的一个别名,通过别名和原来的名字都能找到这个数据;就像一个人可以同时拥有多个不同的绰号,而不同的绰号都指向同一个人,也类似于指针不过与指针大有不同

2.引用的定义

引用的定义格式:数据类型 &别名=原名

int main()
{
	int a=10;
	int& b = a;
	cout << "a=" << a << endl;
	cout << "b=" << b<<endl;
}
结果:
a=10
b=10

可见引用有点类似于指针,都是通过其他方式访问同一个数据,只不过引用定义的符号从*换成了&;不过不同的是指针是存储的变量地址,通过地址去改变这个数据,但是引用是直接改变这个变量本身,所以这里的“引用”可以理解为新提出的一个概念,切记不能理解成取地址

3.注意事项

(1)引用在定义的同时必须初始化,例如以下内容就会报错

int main()
{
	int a=10;
	int& b;
}
报错:
引用 变量 "b" 需要初始值设定项	

因此正确的做法是在定义时就进行初始化,并且要引用一块合法的内存空间

int main()
{
	int a=10;
	int& b=a;
	cout<<"b="<<endl;
}
结果:b=10

(2)引用在初始化之后就不能再改变指向其他数据(有一点指针常量的味道),例如:

int main()
{
	int a=10,b=100;
	int& x=a;
	cout << "x=" <<x<< endl;
	x = b;
	cout << "x=" <<x<< endl;
	cout << "a=" <<a<< endl;
	cout << "x的地址=" << &x << endl;
	cout << "a的地址=" << &a << endl;
}
结果:
x=10
x=100
a=100
x的地址=0000004EF572F724
a的地址=0000004EF572F724

分析上面代码x有一些类似指针,令x指向a然后输出x的值等于10验证了x的确指向了a,那么能改变x的指向吗,就如同指针那样,很明显不行,因为当将b的值赋给x时,a的值也变成了100,就证明代码x=b是赋值操作而没有改变x的指向,等同于将b的值赋给了a因此a的值也改变了成了100;除此之外与指针存放地址不同的是引用是指向数据本身的,从代码的输出当中就能看出;如代码中,我们还可以取他们的地址来进行佐证发现x和a是同一个地址

二、引用作函数参数

在学习引用传参之前我们暂时学了值传递和地址传递,这两种传递方法各有千秋,值传递可以在不想修改数据时进行使用,地址传递可以在要求返回值的时候使用,但是地址传递时使用指针会显得有些麻烦,而相对之下使用引用反而更加便捷和安全,因为引用也可以对外产生影响,并且使用引用时可以不使用*,而使用指针则需要一直使用*;除此之外引用初始化之后就不能修改指向了,相比空指针和野指针会更加安全,那么下面我们就用这三种传递方法写一个交换函数做一个对比:

void swap1(int a,int b)//值传递
{
	int temp = a;
	a = b;
	b = temp;
	cout << "swap1 a=" << a << endl;
	cout << "swap1 b=" << b << endl;
}
void swap2(int* a, int* b)//地址传递
{
	int temp = *a;
	*a = *b;
	*b = temp;
	cout << "swap2 a=" << *a << endl;
	cout << "swap2 b=" << *b << endl;
}
void swap3(int &c,int &d)//引用传递
{
	int temp = c;
	c = d;
	d = temp;
	cout << "swap3 a=" << c << endl;/
	cout << "swap3 b=" << d << endl;
}
int main()
{//每次展示形参后再输出一次实参
	int a = 1,b=2;
	swap1(a, b);
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
	a=1,b=2;
	swap2(&a, &b);
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
	a=1,b=2;
	swap3(a, b);
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
}
结果:
swap1 a=2
swap1 b=1
a=1
b=2
swap2 a=2
swap2 b=1
a=2
b=1
swap3 a=2
swap3 b=1
a=2
b=1

由上面代码不难看出引用传递更加简洁且易于理解,并且形参名不用和实参名保持一致,在传递实参时也很简洁,只有值传递不能改变原数据,地址传递和引用传递都能够改变原数据,但是建议大家在选择传递方法时可以多使用引用传递,不仅安全还简洁易于理解

三、引用做函数返回值

引用不仅可以作为形参,还可以作为函数返回值,甚至是作为左值进行使用,但是要注意函数的返回值不能是局部变量,否则函数进程结束后有可能就会将返回值覆盖掉,所以在返回局部变量时我们可以使用static关键字延长局部变量的生命周期,也就是将局部变量转化成静态局部变量,下面是一个引用做函数返回值的例子:

int& test()
{
	static int a = 10;
	return a;
}
int main()
{
	int& a = test();
	cout << "a=" << a << endl;
	test() = 100;
	cout << "a=" << a << endl;
}
结果:
a=10
a=100

由结果不难看出将函数的返回类型改成int&可以使其成为一个可以改变的左值,若将函数类型改回来变成int,那么就会报错,不仅引用int& a无法正常完成初始化,test函数也无法作为一个左值使用

四、引用的本质

引用的本质在前面的学习当中不难看出有点类似指针常量,即数据类型* const 数据名称,而事实上其底层本质的确是指针常量,而这也符合引用初始化后不能修改的特性

	int a=10;
	int& b = a;//int* const b=&a;
	b = 10;//*b=a;

上方引用内容一旦内部发现是引用,那么就会自动转换成常量指针,正如上方注释,这就是引用的本质;
那么为什么引用的底层是指针还要引进引用呢?事实上C++的发明人也说过他在C++中引进引用的直接目的就是使代码更加美观简洁,就如同使用普通变量一样

五、常量引用

常量引用常用于修饰形参,防止误操作;可能会有人疑惑如果不想函数对于数据只读不改为什么不用值传递,这比引用传递还简洁,事实上这是为了防止空间浪费考虑的,因为在传递一些较小的数据类型比如int、short、char等等也许这种情况并不明显,但是要传递那种数据较大的结构体或者大型对象时就会造成消耗大量的内存和时间,而引用传递只传递对象的引用(或指针),就避免了不必要的拷贝,从而提高了效率;这个时候就要介绍常量引用了:

void show(const int& b)
{
	//b = 20;报错:表达式必须是可修改的左值
	cout << "b=" << b << endl;
}
int main()
{
	//int& a = 10;//报错:非常量引用的初始值必须为左值
	const int& a = 10;//常量引用的正确形式,编译器自动优化代码
	//a = 20;//报错:加入const限定了就不能修改
	int b = 10;
	show(b);
}
结果:
b=10

在这个代码中首先会有人有疑问const int& a=10有什么意义,即便加了const后这代码是合法的但是实际意义好像并不大,事实上这种写法可以避免临时对象的创建:在C++中,如果直接将一个右值(如字面量、表达式的结果等)赋值给一个非引用类型的变量,编译器通常会创建一个临时对象来存储这个值,然而,在这个特定的例子中,由于使用了引用,并且这个引用是const的,编译器允许直接将右值绑定到这个引用上,而不需要创建任何临时对象,这可以减少不必要的内存分配和析构操作,尽管对于像int这样的小类型来说,这种优化可能并不显著;在这个代码中引用传递的只读不写的使用也得到了体现,可以防止代码的误修改

  • 13
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值