C++:引用超详解

C++:引用超详解_第1张图片

文章目录

    • 前言
    • Ⅰ.引用与指针
        • Ⅰ.1.初始化
        • Ⅰ.2.指向
    • Ⅱ.引用与函数参数
        • Ⅱ.1.常量引用
        • Ⅱ.2.临时变量和常量引用参数
        • 1.左值引用
          • 什们时候创建临时变量?
        • 2.右值引用
    • Ⅲ.引用与函数返回值
        • 1.传统返回:
        • 2.引用返回
        • 3.注意事项
    • Ⅳ. 引用和类
    • Ⅴ. 选择地使用引用

前言

引用变量是c++新增的 复合类型(&)。它是已定义变量的别名。例:创建引用变量
程序清单 :

int a=6;
int &b=a;
int c=&a; //注意这一行是将a的地址值赋给c

如此,我们就可以用 a或者引用变量b 来表示该变量。
此时,a和b指向相同的值和内存单元。

作用:主要用作函数的形参,通过引用我们可以将使用原始数据,而不是副本(类似指针)。引用的特性很适合我们处理大型结构


Ⅰ.引用与指针

我们说引用很类似指针,但是它们还是有区别的。

程序清单 1.1:

int a=6;
int &b=a;
int *p=&a;

此时:
a = b = *p =6 ;
&a = &b = p = a的地址;

所以说引用很类似于指针(都是指向同一内存单元),
但是,我们必须要注意的引用和指针的不同点:

1.引用必须 声明时将其初始化
2.引用初始化后不能改变指向

Ⅰ.1.初始化

比如说这样时错误的:
程序清单 1.2:

int a;
int &b;
b=a;

注:并且初始化时不能直接赋值一个常量,就是说我们需要对一个变量引用。 更加直白的来说,是对地址的引用

程序清单1.3:

int &a=6;

这样即使初始化了也是错误的。


Ⅰ.2.指向

引用不能改变它的指向,意思就是更加接近const指针。
其实:
int &b=a;
实际就是:
int * const p=&a;
*p 和 b 都只能指向 a的值和内存单元

为了更好的理解引用不能改变它的指向这一特性,我们看下面这个例子:
程序清单 1.4:

	int a=1,b=0;
	int &c=a;
	c=b;
	cout<<"a:"<<a<<" b:"<<b<<" c:"<<c<<endl;
	cout<<"a:"<<&a<<" b:"<<&b<<" c:"<<&c<<endl;




运行结果:
在这里插入图片描述
我们从结果看出,虽然令 a的引用c等于了b,但是 a和c的地址还是一样的,并且修改c的同时也修改了a。(这个也是我们要求引用声明时就要初始化的理由)


Ⅱ.引用与函数参数

函数的普通参数的特点:按值传递。这样的弊端是我们只能原始数据的副本,在很多场景下是有诸多问题的 — 比如说交换来两个变量的值。

但是我们将引用变量作为参数的时候,就可以使用原始数据。(指针也可以,但我们这里主要讲按引用传递

程序清单 2.1:

void fun(int &a){
     
	a=0;
}
int main(){
     
	int x=1;
	fun(x);
	cout<<x;
	return 0;
}

.
.
.
输出结果:

x =0
我们若是使用普通形参的话,是不能修改原始数据 x的值的。

Ⅱ.1.常量引用

当我我们不想修改数据的值,但是又想使用引用的话,我们可以使用常量引用

void fun(const &a)

当我们试图在函数中修改 a 的值时候,会报错。


Ⅱ.2.临时变量和常量引用参数

程序清单 2.2:

void fun( int &a){
     
...
...
}
int main(){
     
	int x=6;
	double y=66;
	fun(x);
	fun(y);
	fun(6);
	fun(x+6)
}

测试中的三个函数调用只有第一个 fun(x) 可以同通过。
我们前面讲过,只能对变量引用。
参数 6 显然不是变量,x+6同样也不是变量(表达式)。y 类型不匹配,因为形参只能接受 int类型的引用(这里不能强转,下面会讲)。

1.左值引用

解决办法:
我们可以看出 传递引用更加严格,但是后期C++有了函数的引用参数传实参类型不匹配的解决办法:

当且仅当函数的形参为 const 引用时,如果实参与引用形参不匹配,c++会创建实参的临时无名变量

程序清单 2.3:

void fun(const int &a){
     
	...
	...
}
int main(){
     
	int x=6;
	double y=66;
	fun(x);
	fun(y);
	fun(6);
	fun(x+6)
}

现在 实参 y ,6 ,x+6 都不会报错了。
例如 x+6 ,会为 x+6 创建 一个无名的临时变量,初始化为 x+6的值,那么 :a就是这个临时变量的引用。

什们时候创建临时变量?
  1. 函数的形参 必须是 const引用类型
  2. 实参的类型正确,但不是左值。
  3. 实参是左值,并且可以转换为正确的类型。

注:
左值能通过地址访问的值。例如:变量,数组元素,结构成员等都是左值。(常规变量和 const变量都是左值)

右值: 那当然不能地址访问的值。例如:字面常量,多项表达式。(用引号括起来的字符串不是右值,因为由其地址表示)

在这种情况下,编译器会创建一个临时变量,并让形参的引用变量指向它。值得注意的是,临时变量只会在函数调用期间存在

2.右值引用

C++11 新增的一种引用----右值引用。用 && 声明。

程序清单 2.4:

int mian(){
     
	int a=6;
	int & b=a+6;//这是错误的
	int && c=a+6;//正确

Ⅲ.引用与函数返回值

将引用变量作为函数的返回值,称为引用返回。讨论引用返回和传统返回的不同之处。

1.传统返回:

返回机制和 函数按值传递参数很类似

程序清单 3.1:

int add(int a,int b){
     
	a=a+b;
	return a;
}

int main(){
     
	int x=2,y=3,z=0;
	z=add(x,y);
	return 0;
}

返回过程:
返回值a 的值被复制到一个临时位置,然后再将 临时位置的值复制 给目标变量c。(为什们要放在临时位置?因为变量a在函数结束时被销毁。)

:其实函数的传统返回值是一个 右值

2.引用返回

程序清单 3.2:

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

int main(){
     
	int x=2,y=3,z=0;
	z=add(x,y);
	return 0;
}

返回过程:
直接把返回值a 复制到目标变量c。

总结:当我们在传输一些大型结构时,用引用返回效率更高,因为少了复制到临时位置这个步骤。

3.注意事项

下面的程序可以运行吗?
程序清单3.3:

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

int main(){
     
	int x=2,y=3,z=0;
	z=add(x,y);
	return 0;
}

.
.
.
.
不能!!
我们返回a的引用,但是a 在函数运行完毕后也不存在了

当我们想返回引用时,怎们解决这个问题呢?
解决办法:

  1. 向之前的程序 3.2一样,返回一个形参类型为引用的引用变量实参。
  2. 使用new来分配内存,创建变量。(new的详解,点这里)

Ⅳ. 引用和类

基类引用 可以指向 派生类对象,不用强转。

实际作用:
可以 将函数形参声明为 基类引用,则调用函数时的实参 可以是:

  1. 基类对象。
  2. 派生类对象。

注:
当我们参数的数据对象是类对象时,函数形参应该使用 const 引用


Ⅴ. 选择地使用引用

  1. 程序员想修改原始数据的值。
  2. 当函数传递的对象过大时(如结构和类对象),通过传递引用而不是将整个数据对象复制过去,可以提高程序的运行的速度。

你可能感兴趣的:(C++,c++,引用传递)