引用:
与C语言不同之处,c++这门语言多了一个用法——引用(&)
void swap(int l, int r)
{
int temp = l;
l = r;
r = temp;
}
{
int temp = l;
l = r;
r = temp;
}
在C语言中要想实现两个数的调用必须进行传址调用,但是还是上面的代码可以交换两个形参,但是并没有交换实参,只是进行了一份临时拷贝,并没有影响到实参,因此并没有发生交换。我们在c语言中通常进行传址调用,来交换两个数 。
void swap(int l, int r)
{
int temp = l;
l = r;
r = temp;
}
{
int temp = l;
l = r;
r = temp;
}
int main()
{
int a = 10;
int b = 20;
swap(&a, &b);
system("pause");
return 0;
}
{
int a = 10;
int b = 20;
swap(&a, &b);
system("pause");
return 0;
}
这样就是通过传址实现了交换。我们还可以通过指针传递
void swap(int *l, int *r)
{
int temp = *l;
*l = *r;
*r = temp;
}
{
int temp = *l;
*l = *r;
*r = temp;
}
以将实参的地址传给形参(形参得到的依旧是一份拷贝,不过这次是地址的拷贝),然后函数内部就可以通过解引用(*指针名)的方式访问实参所在的空间,进而修改它的值。
但是到了c++,我们可以通过引用来交换
void swap(int &x int &y)
{
int temp = l;
l = r;
r = temp;
}
{
int temp = l;
l = r;
r = temp;
}
将参数定义为引用,这样实参传递过来时就相当于为l和r分别取一个别名,我们直接通过x和y这两个别名进行操作,省去了指针中解引用的操作。
int a=1;
int &b=a;
定义了a的一个引用b。通过对b的修改可以改变a。通过查看变量a和b的地址可以发现它俩拥有同一块空间,即并不为b分配新的内存空间(这里和指针不同)。我们为了简单理解可以把引用称之为取了一个别名。如这里b是a的一个别名,修改b就是修改a。
引用的特性:
1.引用在定义时必须初始化。
2.引用已经引用一个变量不能引用其他的变量。
int a=1;
int &b=a;
int c=2;
int &b=c;//这样就不能通过。
3.引用必须同类型。
4.非常量引用的初始值必须为左值.
非常量引用,即没有加const修饰的引用,初始值必须为左值,NULL是左值吗?显然不是,所以直接将一个普通引用初始化为NULL(这里可以是任意常量)是不可行的(如果可行的话,通过这个引用岂不是可以修改常量了么?这怎么可以,所以不行)。
const修饰的引用:
1.const int &a=0;//const引用常量
2.
double d=0.0;
int &a=d;
在VS2013下编译这条语句会得到这样的提示:
00815C98 movsd xmm0,mmword ptr ds:[81DBB0h]
00815CA0 movsd mmword ptr [d],xmm0
const int &a = d;
00815CA5 cvttsd2si eax,mmword ptr [d]
00815CAA mov dword ptr [ebp-28h],eax
00815CAD lea ecx,[ebp-28h]
00815CB0 mov dword ptr [a],ecx
无法用 "double" 类型的值初始化 "int &" 类型的引用(非常量限定).
但是加上const,如下:
double d=0.0;
const int &a=d;
没有报错,我们查看反汇编得到以下代码:
double d = 0.0;
00815C98 movsd xmm0,mmword ptr ds:[81DBB0h]
00815CA0 movsd mmword ptr [d],xmm0
const int &a = d;
00815CA5 cvttsd2si eax,mmword ptr [d]
00815CAA mov dword ptr [ebp-28h],eax
00815CAD lea ecx,[ebp-28h]
00815CB0 mov dword ptr [a],ecx
运行到const int &a= d;语句时,(前两行生成一个d的临时值,后两行)并不是简简单单得到将a初始化为d的引用,而是产生了一个临时变量,而临时值是具有常属性,所以才可以赋给常引用 a。
要是这样:
double d = 0.0;
const double &a = d;
const double &a = d;
定义了一个常引用,使得 d 的值不能通过 a来修改(这种方法经常用到)通常会用在函数传参中,传给只需要访问d而不需要修改d的值的函数,保障了 d 的安全性。
查看反汇编:
double d = 0.0;
00C65C98 movsd xmm0,mmword ptr ds:[0C6DBB0h]
00C65CA0 movsd mmword ptr [d],xmm0
const double &a = d;
00C65CA5 lea eax,[d]
const double &a = d;
00C65CA8 mov dword ptr [a],eax
00C65C98 movsd xmm0,mmword ptr ds:[0C6DBB0h]
00C65CA0 movsd mmword ptr [d],xmm0
const double &a = d;
00C65CA5 lea eax,[d]
const double &a = d;
00C65CA8 mov dword ptr [a],eax
引用大小:
一个引用的大小与它所引用的对象的类型有关。
例如前一个例子中的引用:const double &a= 0; 那么sizeof(a)的大小将会是sizeof(double)的大小,在32位平台上,这个值是8。
引用作为返回值:
- int & fun()
- {
- int a = 10;
- return a;
- }
比如我们再main函数中这样调用这个函数:int &b = fun();
有没有问题?仔细想想,可以发现,变量a的空间是在属于fun函数的栈桢上开辟的,变量a在fun函数返回后被销毁,此时它的值不在有效,也不能保证其值不变,返回这个变量的引用,相当于返回了栈空间的地址,那么b所引用的空间已被释放,它的值将会变得不可预料。当然指针的使用中也存在这样的错误例子(返回了栈空间地址)。
我们换个方式接受一下函数的返回值:int b = fun(); 这样依旧会存在一定的问题,函数的返回值是个引用(底层实现为指针),而函数返回值是通过寄存器实现的,则函数调用完成后,变量a的地址将被保存到寄存器中用以返回,然后函数栈桢销毁,再把这个地址的值赋给变量b。在此过程中也不能保证a所在空间的内容不发生改变,所以依旧有错。