1.介绍
引用是c++对c的重要扩充。在c/c++中指针的作用基本都是一样的,但是c++增加了另外一种给函数传递地址的途径,这就是按引用传递(pass-by-reference),它也存在于其他一些编程语言中,并不是c++的发明。
- 变量名实质上是一段连续内存空间的别名,是一个标号(门牌号)
- 程序中通过变量来申请并命名内存空间
通过变量的名字可以使用存储空间
1.1 简介
- 引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。
- 引用的声明方法:类型标识符 &引用名=目标变量名;
2.示例
int a;
int &ra=a; //定义引用ra,它是变量a的引用,即别名
- 1.&在此不是求地址运算,而是起标识作用。
- 2.类型标识符是指目标变量的类型。
- 3.声明引用时,必须同时对其进行初始化。
- 4.引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名, ra=1; 等价于 a=1;
- 5.声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。&ra与&a相等
- 6.引用和引用对象的内存地址是相同的。
3.引用传参
void myswap(int &a, int &b)
{
int t = a;
a = b;
b = t;
}
int main()
{
int a = 10;
int b = 20;
myswap(a, b);
cout << a << endl << b << endl;
return 0;
}
上述程序运行时,如果输入数据10 20并回车后,则输出结果为20 10。
引用相当于是外部变量的别名,实际操作的就是该变量,即在函数内对该变量进行修改的话,在外部该变量也会相应被修改。
- 1.传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。
- 2.使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给 形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效 率和所占空间都好。
- 3.使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的 形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。
4.数组引用
int main()
{
//方法1
int arr[10]={0,1,2,3,4,5,6,7,8,9};
int(&f)[10] = arr;
for (int i = 0; i < 10; i++){
cout << f[i] << " ";
}
cout << endl;
//方法2
typedef int kk[10];
kk& ff= arr;
for (int i = 0; i < 10; i++){
cout << ff[i] << " ";
}
cout << endl;
return 0;
}
5.常量引用
常引用声明方式:const 类型标识符 &引用名=目标变量名;.
const Type**&** ref **=** val**;**
用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为const,达到了引用的安全性。
int a ;
const int &ra=a;
ra=1; //错误
a=1; //正确
void test01(){
int a = 100;
const int& aRef = a; //此时aRef就是a
//aRef = 200; 不能通过aRef的值
a = 100; //OK
cout << "a:" << a << endl;
cout << "aRef:" << aRef << endl;
}
void test02(){
//不能把一个字面量赋给引用
//int& ref = 100;
//但是可以把一个字面量赋给常引用
const int& ref = 100; //int temp = 200; const int& ret = temp;
}
- 1.常量引用主要用在函数的形参,尤其是类的拷贝/复制构造函数。
- 2.将函数的形参定义为常量引用的好处:
- 3.引用不产生新的变量,减少形参与实参传递时的开销。
- 4.引用可能导致实参随形参改变而改变,将其定义为常量引用可以消除这种副作用。
- 5.如果希望实参随着形参的改变而改变,那么使用一般的引用,如果不希望实参随着形参改变,那么使用常引用。
6.引用作为函数返回值
要以引用返回函数值,则函数定义时要按以下格式:
类型标识符 &函数名(形参列表及类型说明)
{函数体}
- 1.以引用返回函数值,定义函数时需要在函数名前加&
- 2.用引用返回一个函数值的最大好处是,在内存中不产生被返回值的副本。
int& fun()
{
//int a = 10;//这个例子是错误的,fun函数运行完毕后,不变量a会被销毁,但由于编译器不用,所以第一次打印值为10,所以应加上static修饰
static int a = 10;
return a;
}
int main()
{
int& b = fun();
cout<<b<<endl;
cout<<b<<endl;
return 0;
}
7.指针引用
在c语言中如果想改变一个指针的指向而不是它所指向的内容,函数声明可能这样:
void fun**(**int***\*);**
Type***** pointer **=** **NULL;**
Type***&** **=** pointer**;**
struct Teacher{
int mAge;
};
//指针间接修改teacher的年龄
void AllocateAndInitByPointer(Teacher** teacher){
*teacher = (Teacher*)malloc(sizeof(Teacher));
(*teacher)->mAge = 200;
}
//引用修改teacher年龄
void AllocateAndInitByReference(Teacher*& teacher){
teacher->mAge = 300;
}
void test(){
//创建Teacher
Teacher* teacher = NULL;
//指针间接赋值
AllocateAndInitByPointer(&teacher);
cout << "AllocateAndInitByPointer:" << teacher->mAge << endl;
//引用赋值,将teacher本身传到ChangeAgeByReference函数中
AllocateAndInitByReference(teacher);
cout << "AllocateAndInitByReference:" << teacher->mAge << endl;
free(teacher);
}
8.引用本质
引用的本质在c++内部实现是一个指针常量
Type& ref = val; // Type* const ref = &val;
c++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同,只是这个过程是编译器内部实现,用户不可见。
//发现是引用,转换为 int* const ref = &a;
void testFunc(int& ref){
ref = 100; // ref是引用,转换为*ref = 100
}
int main(){
int a = 10;
int& aRef = a; //自动转换为 int* const aRef = &a;这也能说明引用为什么必须初始化
aRef = 20; //内部发现aRef是引用,自动帮我们转换为: *aRef = 20;
cout << "a:" << a << endl;
cout << "aRef:" << aRef << endl;
testFunc(a);
return EXIT_SUCCESS;
}
引用总结
- 1.在引用的使用中,单纯给某个变量取个别名是毫无意义的,引用的目的主要用于在函数参数传递中,解决大块数据或对象的传递效率和空间不如意的问题。
- 2.用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率,且通过const的使用,保证了引用传递的安全性。
- 3.引用与指针的区别是,指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。
- 4.使用引用的时机。流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。