一 引用(普通引用)
变量名实质上是一段连续存储空间的别名,是一个标号(门牌号)
程序中通过变量来申请并命名内存空间。
通过变量的名称我们可以使用内存空间。
那么我们对一段内存空间我们只能使用多个别名,通过多个别名对内存空间进行操作。(引用)因此我们可以将引用看做一个已定义变量的别名。引用在作为函数参数声明变量时不进行初始化。
int main(void)
{
int a = 10;
int &b = a;
a = 11;
{
int *p = &a;
*p = 12;
cout << "a= "<<a << endl;
}
b = 14;
cout << "a= " << a << endl;
system("pause");
return 0;
}
输出 a=12
a=14
引用是我们c++的语法范畴,当我们看到引用的语法时,我们就需要用c++编译器来考虑问题而不是c编译器。上面代码中,我们发现,引用的可以完成指针的部分功能。
二 引用作函数参数
普通引用在声明时必须用其他的变量进行初始化,引用在作为函数参数声明变量时不进行初始化。
#include<iostream>
using namespace std;
int main(void)
{
int a = 10;
int &b = a;
int &c; //报错 引用未初始化;
system("pause");
return 0;
}
引用做函数参数有许多好处,我们先看一个简单数据类型引用的的事例
void myswap(int a, int b)
{
int c = 0;
c = a;
a = b;
b = c;
}
上面代码我们本意是完成a和b的值得交换,但是显然我们这样做不对,在没有引用之前我们可以使用指针来完成这个函数,现在我们可以使用引用完成。
void myswap(int &a, int &b) //引用作函数参数不用初始化辅助
{
int c = 0;
c = a;
a = b;
b = c;
}
int main(void)
{
int a = 10;
int b = 20;
myswap(a, b);
cout << "a= " << a << endl;
cout << "b= " << b << endl;
system("pause");
return 0;
}
输出结果 a=20
b=10
#include<iostream>
using namespace std;
struct Teacher
{
char name[64];
int age;
};
void printfT(Teacher *pT)
{
cout << pT->age << endl;
}
void printfT2(Teacher &pT)
{
cout << pT.age << endl;
}
void printfT3(Teacher pT)
{
cout << pT.age << endl;
}
int main(void)
{
Teacher t1;
t1.age = 35;
printfT(&t1);
printfT2(t1); //pT是t1的别名,在函数中修改pT相当于修改了t1;
printfT3(t1); //pT是形参 ,t1被copy到pT中,在这个函数中修改pT,不会修改t1;
system("pause");
return 0;
}
三 引用的意义
1、 引用作为其他变量的别名而存在,因此在一些场合可以替代指针
2、引用相对于指针来讲具有更好的可读性,和实用性。
四 引用的本质
单独引用时,必须初始化,这说明他很像一个常量
int a =10;
int &b=a;
其中b相当于一个常指针,int *const b(即指针的值不可修改,指针所指向的内存空间的只可以修改)
struct teacher
{
char name[64]; //占用64个字节
int age; //占用4个字节
int &a; //占用4个字节
};
为了验证引用在内存空间中占用字节,我们使用sizeof(teacher)得出72个字节,这说明引用在内存空间和指针一样占用内存。
引用在C++中的内部实现是一个常指针 Type& name çèType*const name C++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同。
当我们看到一个引用作函数参数时,如下:
void func(int &a)
{
a = 5;
}
我们首先要考虑到这是在c++编译器,而不是在c编译器中,在c++编译器中 int &a 会被等价于 int *const a。对a = 5,当c++编译器看到a是一个引用时就会变为 *a = 5;实际代码如下:
void func(int * const a)
{
*a = 5;
}
间接赋值的三个条件:
1、定义两个变量(一个实参一个形参)
2、建立关联,实参取地址传给形参
3、*p形参去间接去修改实参的值
引用在实现上,只不过是把:间接赋值成立的三个条件的后两步和二为一,当实参传给形参引用时,只不过是c++编译器帮我们程序员手工取了一个实参地址,传给形参引用(常量指针)。
五、函数返回值是引用
一、返回数据是基础数据类型
含糊返回值是引用,实际上是将变量的地址返回了出去。但是,函数的调用过程,也就是函数的压栈过程,函数调用完毕,所有在栈上申请的内存都会被释放,因此函数的返回值当引用时,必须注意:
若返回栈变量不能当做左值,也不能作为其他引用的初始值(简单数据类型的值是可以传递出去的)。
若返回静态变量或全局变量,可以当右值也可当做值。
#include<iostream>
using namespace std;
//返回变量的值
int GetAA1()
{
int a;
a = 10;
return a;
}
//返回变量的本身,即变量的地址
//基础类型a返回时会有一个类似a的副本,所有a的值可以用普通变量去接收。//有一点点类似匿名对象
int &GetAA2()
{
int a;
a = 10;
return a;
}
int main()
{
int a1 ,a2;
a1 = GetAA1();
a2 = GetAA2(); //使用一个变量来接收返回引用,会将引用的值赋给变量
int &a3 = GetAA2();//使用引用去接收一个函数返回值引用,即const
cout << a1 << " " << a2 << " " << a3 << endl;
system("pause");
}
DUG 下的64位进行编译输出(Realse下输出无误):
10 10 -858993460
请按任意键继续. . .
二、返回数据是非基础数据类型
当函数的返回值不仅是一个引用,而且返回值的数据类型是一个类或者是一个结构体的时候,这个时候我们返回引用是栈变量还是全局或是静态变量的结果与上面一样,唯一不一样的是我们需要用到了浅拷贝与深拷贝。