欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++引用总结

程序员文章站 2022-05-13 21:18:47
...

引用:

  • C++新增了一种复合类型——引用变量。引用是已定义的变量的别名,新名字与旧名字有相同的值和内存地址。
创建引用变量
  • 创建引用格式:type &new_varname = old_var;(调用引用必须要初始化)
int old;
int &newname;
newname = old;//这种方式是不行的
  • 引用不光可以引用标识符(变量等),也可以引用立即数(右值、字面值、常量),但必须要加const(例如:int const &var = 40;),即常量引用。
  • 引用是一次性的,一旦与某个变量关联起来,就将一直效忠于它,无法再次更改。

看个例子

 int rats = 101;
 int* p = &rats;
 int &rodent = *p;
 int bunnies = 50;
 p = &bunnies;
 cout << rodent << endl;

程序运行结果为101,说明rodent引用的是rats。(*p为rats所在的内存地址的内容,引用*p即引用rats),若要引用指针则要这样定义:int* &rodent = p;

  • 不能空引用,引用使用不当会产生野引用(悬空引用)。
  • 引用可以当做函数的参数,它引用的对象就是函数的实参(函数将可以使用原始数据,而不是数据值的拷贝),这种传递参数的方法被称为引用传递
      优点:
        a.函数之间可以共享参数
        b.提高参数的传递效率(比指针还要高)
        c.可以获取并修改参数

为了更好的比较,下面通过一个常见的问题——交换两个变量的值,对使用引用、指针和值传递做一下比较。跳转代码

引用的属性和特别之处
#include <iostream>
//计算参数的立方
double cube(double a)//值传递
{
    a* = a*a;
    return a;
}

double refcube(double &ra)//引用传递
{
    ra *= ra* ra;
    return ra;
}

int main()
{
    using namespace std;
    double x = 3.0;
    cout << cube(x) << " = cube of " << x << endl;
    cout << refcube(x) << " = cube of " << x << endl;
    return 0;
}

我们知道,refcube()是一个引用传递的函数,那么计算立方后,x经过引用传递应该也会变成27,可是结果却是:
C++引用总结
其实这不是因为引用传递失败了,造成这样的原因是由于cout的特性:参数从右到左入栈,即x还在x=3时就先入栈了。
  回到引用,实际上refcube函数使得x的值变成了27,如果程序员的意图是让函数使用传递给它的信息,而不对这些信息进行修改,同时又想使用引用,则应该使用常量引用,即函数应该定义为:

double refcube(const double &ra) 
{
    ...
    //但此时不能再修改ra的值,ra是一个只读形参
}

所以,如果要编写类似上述类型的代码(即使用基本数据类型),应采用值传递的方式。当数据比较大(如结构和类)时,引用参数将很有用。

  • 按值传递的函数,可以使用多种类型的实参,以上述的cube为例:
double z;
z = cube(x+2.1);//1
z = cube(3.14);//2
int y = 10;
z = cube(y);//3

以上的调用都是合法的

  • 但对于refcube(double &ra)来说,如果ra是一个变量的别名,则实参也应该是变量。下面的代码就不合理——表达式x+3.0并不是变量
double num = refcube(x+3.0);
将引用用于结构和类

实际上,引入引用主要是为了用于这些类型,使用结构(类)引用参数的方式与使用基本变量引用相同。假设有如下结构定义:

struct student
{
    char name[20];
    short age;
    int roomnum;
};

则可以这样编写函数原型,在函数中将指向该结构的引用作为参数

void change_room(student & stu);

实例一下:

void change_room(student & stu)
{
    stu.roomnum = 0;
}

student stu1 = {"小明"16,3209};//初始化一个学生结构变量
change_room(stu1);//调用函数

由于change_room(stu1)的形参stu为引用,因此stu指向了stu1,函数体设置了成员stu1.roomnum,就这里而言按值传递不可行,因为这将导致设置的是stu1的临时拷贝的成员roomnum。

如果不希望函数修改传入的结构,可使用const关键字

void show(const student & stu)
{
    std::cout << stu.name << stu.age << stu.roomnum << std::endl;
}

由于show()显示的是结构的内容而不修改它,就这个函数而言,也可按值传递,但与复制原始结构的拷贝相比,使用引用可节省时间和内存。

将引用用于返回值
  • 为何要返回引用?
    传统返回机制与按值传递函数参数类似:计算关键字return后面的表达式,并将结果返回给调用函数。从概念上来说,这个值被复制到一个临时位置,而调用程序将使用这个值。
    如果函数返回一个结构,而不是指向结构的引用,将会先把整个结构复制到一个临时位置,再将这个结构拷贝复制给调用的程序。但在返回值为引用时,将直接把源结构复制到目标结构。
  • 返回引用时需要注意的问题:
    返回引用时最重要的一点:避免返回函数终止时不再存在的内存单元引用,即返回局部变量(或指向局部变量的指针)的引用。
    解决方法有:1.返回一个作为参数传递给函数的引用。
          2.用new来分配新的存储空间。


交换函数比较

#include <iostream>

class swaptest
{
public:
    int x;
    int y;
    swaptest(int a,int b):x(a),y(b) { }//构造函数初始化类成员x,y
    void swapv(int a,int b)
    {
        int temp = a;
        a = b;
        b = temp;
    }
    void swapp(int* a,int* b)
    {
        int temp = *a;
        *a = *b;
        *b = temp;
    }
    void swapr(int &a,int &b)
    {
        int temp = a;
        a = b;
        b = temp;
    }
    ~swaptest(){ }
};

int main(int argc, char const *argv[])
{
    using std::cout;
    using std::endl;

    swaptest test(3,10);
    cout << test.x << " "<< test.y << endl;

    test.swapv(test.x,test.y);
    cout << test.x << " " << test.y << endl;

    test.swapp(&test.x,&test.y);
    cout << test.x << " " << test.y << endl;

    test.swapr(test.x,test.y);
    cout << test.x << " " << test.y << endl;
    return 0;
}

执行结果

各函数执行后结果 变量x 变量y
初始 3 10
swapv()值传递 3 10
swapp()指针 10 3
swapr()引用 3 10

可以看到,只有值传递的函数没有交换变量的值。引用和指针都成功交换了变量的内容。

  • 先看值传递。swapv函数的操作是将x,y进行对调。需要注意的是,对形参的操作不会影响到a,b。当test.x,test.y把值赋给a,b之后,对a,b不论再做什么操作,都不会影响到test.x,test.y本身。
swapp(int *a, int *b)//函数声明

swapp(&test.x, &test.y)//函数调用
  • 再看地址传递,如上。注意,这时的函数的声明和调用的写法与值传递不同。
    但是与值传递的分析一样,我们可以设想,在swapp函数里,a=&test.x; b=&test.y;表示test.x的地址代入到了a,test.y的地址代入到了b。这样一来,对a, b的操作就是test.x,test.y本身的操作。所以test.x,test.y的值被对调了
swapr(int &a, int &b)// 函数声明

swapr(test.x, test.y)//函数调用
  • 引用传递的函数声明和调用,如上。前面说过,在定义引用变量时必须对其进行初始化。实际上,swapr()函数调用时使用实参test.x,test.y初始化了形参a,b,因此函数的引用参数被初始化为函数调用传递的实参。所以,对引用参数a,b的操作实际上就是对test.x,test.y的直接操作。
    返回
相关标签: 引用