目录
一、引用的基本语法
引用介绍
引用说明
二、引用的注意事项
三、引用做函数参数
四、引用做函数的返回值
五、引用的本质
六、常量引用
一、引用的基本语法
引用介绍
C++是C语言的继承,它可进行过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行以继承和多态为特点的面向对象的程序设计。引用(reference)就是C++对C语言的重要扩充。引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。引用的声明方法:类型标识符 &引用名=目标变量名;
引用引入了对象的一个同义词。定义引用的表示方法与定义指针相似,只是用&代替了*。
引用说明
(1)&在此不是求地址运算,而是起标识作用。
(2)类型标识符是指目标变量的类型。
(3)声明引用时,必须同时对其进行初始化。
(4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。
|
a为目标原名称,ra为目标引用名。给ra赋值:ra=1; 等价于 a=1;
(5)对引用求地址,就是对目标变量求地址。&ra与&a相等。即我们常说引用名是目标变量名的一个别名。别名一词好像是说引用不占据任何内存空间。但是编译器在一般将其实现为const指针,即指向位置不可变的指针。即引用实际上与一般指针同样占用内存。
(6)不能建立引用的数组。因为数组是一个由若干个元素所组成的集合,所以无法建立一个由引用组成的集合。但是可以建立数组的引用.
例如: int& ref [3]= {2,3,5};//声明ref引用的数组错误
但是可以这样写:
1 2 |
|
为什么要加上const ,因为{2,3,5}此时是个字面值数组,是保存在代码段里,只读的属性,如果不加,编译错误,而且后面对ref[0]的赋值也不会成功.
需要特别强调的是引用并不产生对象的副本,仅仅是对象的同义词。因此,当下面的语句执行后:
pt1.offset(12,12);
pt1和pt2都具有(12,12)的值。
引用必须在定义时马上被初始化,因为它必须是某个东西的同义词。你不能先定义一个引用后才
初始化它。例如下面语句是非法的:
Point &pt3;
pt3=pt1;
引用的本质就是给一个变量起别名
语法:数据类型 &别名 = 原名
图示:
示例:
#include<iostream>
using namespace std;
int main()
{
// 引用基本语法
// 数据类型 &别名 = 原名
int a=10;
// 创建引用
int &b=a;
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
b=100;
cout<<"通过b修改后a,b的值"<<endl;
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
return 0;
}
运行结果:
二、引用的注意事项
引用必须初始化
引用在初始化后,不可以改变
图示:
示例:
#include<iostream>
using namespace std;
int main()
{
int a=10;
int &b;
// 1、引用必须初始化
// 2、引用在初始化后,不可以改变
return 0;
}
错误显示:
示例:
#include<iostream>
using namespace std;
int main()
{
int a=10;
// 1、引用必须初始化
int &b=a;
// 2、引用在初始化后,不可以改变
int c=20;
b=c; // 赋值操作,而不是更改引用
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
cout<<"c="<<c<<endl;
return 0;
}
运行结果:
三、引用做函数参数
作用:函数传参时,可以利用引用的技术让形参修饰实参
优点:可以简化指针修改实参
总结:通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单。
示例:
#include<iostream>
using namespace std;
// 交换函数
// 1、值传递
void mySwap01(int a,int b)
{
int temp=a;
a=b;
b=temp;
cout<<"值传递中a="<<a<<endl;
cout<<"值传递中b="<<b<<endl;
}
// 2、地址传递
void mySwap02(int *a,int *b)
{
int temp=*a;
*a=*b;
*b=temp;
cout<<"地址传递中a="<<*a<<endl;
cout<<"地址传递中b="<<*b<<endl;
}
// 3、引用传递
void mySwap03(int &a,int &b)
{
int temp=a;
a=b;
b=temp;
cout<<"引用传递中a="<<a<<endl;
cout<<"引用传递中b="<<b<<endl;
}
int main()
{
cout<<"值传递"<<endl;
int a=10;
int b=20;
mySwap01(a,b);// 值传递
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
cout<<endl<<"地址传递"<<endl;
mySwap02(&a,&b);// 地址传递
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
cout<<endl<<"引用传递"<<endl;
mySwap03(a,b);// 引用传递,形参会修饰实参的
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
return 0;
}
运行结果:
四、引用做函数的返回值
作用:引用是可以作为函数的返回值存在的
注意:不要返回局部变量引用
用法:函数调用作为左值
示例:
#include<iostream>
using namespace std;
// 引用做函数的返回值
// 1、不要返回局部变量的引用
int& test01()
{
int a=10; // 局部变量存放在四区中的栈区
return a;
}
// 2、函数的调用可以作为左值
int& test02()
{
static int a=10;// 静态变量,存放在全局区,全局区上的数据在程序结束后由系统释放
return a;
}
int main()
{
int &ref=test01();
cout<<"ref="<<ref<<endl;// 第一次结果正确,是因为编译器对栈区返回的数据做了保留
cout<<"ref="<<ref<<endl;// 第二次结果错误,因为a的内存已经释放
int &ref2=test02();
cout<<"ref2="<<ref2<<endl;
cout<<"ref2="<<ref2<<endl;
test02()=1000;// 如果函数的返回值是引用,这个函数调用可以作为左值
cout<<"ref2="<<ref2<<endl;
cout<<"ref2="<<ref2<<endl;
return 0;
}
test01错误显示:
运行结果:
五、引用的本质
本质:引用的本质在C++内部实现是一个指针常量
结论:引用的本质是指针常量,但是所有的指针操作编译器都帮我们做好了,不需要解引用
示例:
#include<iostream>
using namespace std;
// 发现是引用,转换为int* const ref =&a;
void func(int &ref)
{
ref=100;//ref是引用,转换为 *ref=100;
}
int main()
{
int a=10;
// 自动转换为 int* const ref=&a;指针常量是指针指向不可以更改,也说明为什么引用不可以更改
int &ref=a;
ref=20;// 内部发现ref是引用,自动帮我们转换为: *ref=20;
cout<<"a="<<a<<endl;
cout<<"ref="<<ref<<endl;
func(a);
cout<<"a="<<a<<endl;
cout<<"ref="<<ref<<endl;
return 0;
}
运行结果:
六、常量引用
作用:常量引用主要用来修饰形参,防止误操作
在函数参数列表中,可以加const修饰形参,防止形参改变实参
示例:
#include<iostream>
using namespace std;
// 打印数据函数
void showValue(const int &val)
{
//val=1000;// 报错,必须是可修改的左值
cout<<"val="<<val<<endl;
}
int main()
{
// 常量引用
// 使用场景:用来修饰形参,防止误操作
// int a=10;
// int &ref=10;// 引用必须引用一块合法的内存空间
// 加上const之后,编译器将代码修改,int temp =10;const int &ref=temp;
const int &ref=10;
// ref=20;// 加入const之后变为只读,不可以修改
int a=100;
showValue(a);
cout<<"a="<<a<<endl;
return 0;
}
形参列表加入const之后,报错显示
运行结果: