你的位置:首页 > 信息动态 > 新闻中心
信息动态
联系我们

C++引用深度剖析

2021/12/20 11:33:41

1. 引用

        引用是C++中的概念,初学者容易把引用和指针混淆一起。一下程序中,n是m的一个引用(reference),m是被引用物(referent)。   

int m;

int &n = m;

        n相当于m的别名(绰号),对n的任何操作就是对m的操作。例如有人名叫王小毛,他的绰号是“三毛”。说“三毛”怎么怎么的,其实就是对王小毛说三道四。所以n既不是m的拷贝,也不是指向m的指针,其实n就是m它自己。

1.1 引用的定义

引用的概念:     

      引用就是 C++语言中对一个变量或常量标识符起的别名。

引用的说明:

      说明一个引用时,在引用标识符的前面   加上‘&’,后面跟上它所引用的标识符的名字。 如:

int val;                   //定义一个整型变量val

int &rval=val;            // rval是对val的引用

引用说明时必须初始化:

       引用不是变量,所以必须在说明引用时说明它所引用的对象。所引用的对象必须有对应的内存空间。

实例:

注:变量val和rval完全相同,rval只是val的一个别名。

 1.2 引用的数据类型

1.2.1 能够被引用的数据类型:

   ① 对简单数据类型变量或常量的引用

    如 int &、 char &、 float &、double &、bool &等;

   ② 对结构类型变量或常量的引用

    如 对class类和struct结构体,容器vector等进行引用;

   ③ 对指针变量或常量的引用     如下代码:

int *pInt = new int;

int *&rpInt = pInt;

*rpInt = 45.6;

delete rpInt;

1.2.2 不能够被引用的数据类型:

   ①不能对void进行引用

   因为void本身就表示没有数据类型,对它的引用没有意义。

  ②不能对数组名引用。 如下代码:

int arr[10];        

int& rarr1 = arr;      //(×)

int& rarr2 = arr[0];  //(√)

 ③不能定义指向引用的指针,  如:

int i;       
int& ri = i;       
int& *pr = &ri;   //(×)

注: int﹡& 和 int& ﹡的区别:

int﹡&表示对int型指针的引用

int& ﹡表示指向int型引用的指针

1.3 const引用

 1.在引用说明前加const。

2.不能改变const引用的值,但是可以改变它所引用的对象的值。   如:     

 int i;

const int& ri = i;

ri = 45;  //(×)    

i = 45;   //(√)

3.对一个常量进行引用时,必须将这个引用定义为const引用。  如: 

const int ci = 45;     

int& ri = ci;          //(×)

const int& rci = ci;   //(√)

1.4 指针和引用

例如:           

int i = 10;

int *pi = &i;     // pi是指向i的指针

int &ri = i;      // ri是对i的引用

pi、 ri在内存的示意图如下:

 注:指针变量具有独立的内存空间存放变量的值,而引用只是一个依附于被引用变量的符号,没有独立的内存空间。

共同点:

指针和引用都是对某一个变量所代表的空间进行间接操作的手段。

☆指针是通过存放变量空间的地址来达到间接操作的目的;

☆引用是通过为变量定义别名来达到间接操作的目的;

不同点:

指针指向一块内存,它的内容是所指内存的地址;

而引用则是某块内存的别名,引用不改变指向。

 区别(指针):

1.指针通过间接访问操作符*来访问;

如 *pi = 40;

2.可以改变指针的值;

如    int j;

      pi=&j;

3.指针可以为空

4.指针可以被重新赋值以指向另一个不同的对象,因此使用指针之前必须做判空操作。

5. sizeof 指针”得到的是指针本身的大小。

6.指针可以改变指向,而指向其它对象。

指针可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。

7.指针使用不安全。对于指针来说,它可以随时指向别的对象,并且可以不被初始化,或为NULL,所以不安全。const 指针虽然不能改变指向,但仍然存在空指针,并且有可能产生野指针。

区别(引用):

1.引用直接访问它所引用的空间;

如  ri = 40;

2.引用一旦被初始化后,就不能改变。所有对引用的操作都被认为是对它所引用的变量的操作int &ri = i;

如  ri=j;     /﹡表示的不是ri改为对j的引用,而表示将j的值赋给ri所引用的变量i  ﹡/

3.引用不能为空;

4.引用只能在定义时被初始化一次,之后不可变。引用总是指向在初始化时被指定的对象,以后不能改变。

5. sizeof 引用”得到的是所指向的变量(对象)的大小。

6.引用不可以改变指向,但是可以改变初始化对象的内容;引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的。

7.由于不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用,因此引用很安全

1.5 引用&和取地址&的区别

定义:(int a;)

1、int &b = a;      //b为a的引用

2、int *p = &a;   //取变量a的地址赋给指针变量p

区分:

1.出现在变量声明语句中位于变量的左边时,声明为引用;

2.给变量赋初值时出现在等号的右边或在执行过程中作为一元运算符出现表示取对象的地址。

用途:

1.引用最重要的用途就是作为函数参数,进行传值;

2.取地址的实际作用就是获取对象的首地址;

注:和类型在一块的是引用;

       和变量在一块的是取地址。

1.6 常量指针和常量引用

定义

1、指向常量的指针,在指针定义语句的类型前加const,表示指向的对象是常量。

定义指向常量的指针只限制指针的间接访问操作,而不能规定指针指向的值本身的操作规定性。

用途

int i = 10;

const int *pointer = & i;

*pointer = 20;             //( 错误)

注:此时的*pointer为常量,不能再对此进行赋值处理。 

定义

2、指向常量的引用,在引用定义语句的类型前加const,表示指向的对象是常量。

指向常量的引用也跟指向常量的指针一样不能利用引用对指向的变量进行重新赋值操作。

用途

int i = 10;

const int &ref = i;

ref= 20;       // (错误)

注:此时的ref为常量,不能再对此进行赋值处理。

1.7 引用的应用

引用最大的用途是作为函数的参数或返回值类型。

1.7.1 引用参数通常使用的场合:

1、使用引用传递可变参数

2、给函数传递结构或类的对象

3.使用引用作为函数返回值

1.7.2 使用引用传递可变参数:

      传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。

1.7.3 使用引用传递可变参数

void swapint(int &a, int &b)

{

     int temp;

     temp = a;

     a = b;

     b = temp;

}

注:调用该函数的c++方法为:swapint(x,y); c++自动把x,y的地址作为参数传递给swapint函数。

1.7.4 给函数传递结构或类的对象

        当结构或者类的对象被传递给函数时,使用引用参数可使参数传递效率得到提高,因为引用并不产生对象的副本,也就是参数传递时,对象无须复制。

void WriteDataToTxt(CString  &strFilePath,  FileInfoStruct &FileInfo);

注:此函数将Cstring类的对象引用和结构体类的对象的引用作为函数形参传递到主调函数中去。

1.7.5 使用引用作为函数返回值

如果一个函数返回了引用,那么该函数的调用也可以被赋值。例如:

double &max(double &d1, double &d2)

{

    return d1>d2?d1:d2;

}

        由于max()函数返回一个对双精度数的引用,那么我们就可以用max() 来对其中较大的双精度数加1

   max(x,y) += 1.0;

1.8 值传递、引用传递、指针传递

1.8.1 值传递

 1.8.2 指针传递

1.8.3 引用传递

值传递方式:

        由于被调用函数体内的变量x,y是主调函数中外部变量a,b的一份拷贝,改变x,y的值不会影响到a,b,所以a,b的值不会改变。

指针传递方式:

        被调用函数体内的x,y是指向主调函数中外部变量a,b的指针,改变该指针的内容将导致a,b的值改变,所以a,b的值会改变。

引用传递方式:

        由于被调用函数体内的x,y是主调函数中外部变量a,b的引用,x,y和a,b是同一个东西,改变x,y相当于改变a,b,所以a,b的值会改变。

        值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。(这里是在说实参指针本身的地址值不会变)

      指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。

       在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。

        指针本身的值(地址值)是以pass by value进行的,你能改变地址值,但这并不会改变指针所指向的变量的值。

        引用本身是以pass by reference进行的,改变其值即改变引用所对应的变量的值。

       在以下情况下你应该使用指针,一是你考虑到存在不指向任何对象的可能(在这种情况下,你能够设置指针为空),二是你需要能够在不同的时刻指向不同的对象(在这种情况下,你能改变指针的指向)。

       如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,那么你应该使用引用。

1.8.4 总结

        1.指针传递参数时,指针中存放的也是实参的地址,但是在被调函数内部指针存放的内容可以被改变,即可能改变指向的实参,所以并不安全,而引用则不同,它引用的对象的地址一旦赋予,则不能改变。

       2. 指针传递只是传了一个地址copy, 在函数内部改变形参所指向的地址,不能改变原实参指向的地址,仅可以通过修改形参地址的内容,来达到修改实参内容的目的。

        3.如果想通过被调函数来修改原实参的地址或给重新分配一个对象都是不能完成的,只能使用双指针或指针引用。