对C++引用的理解

C++中引用是个非常重要的概念,引用是在C++中新增的一种复合类型,是已定义的变量的别名。但是这种别名有什么作用呢?难道只是为了给变量起个绰号?并不是,引用变量的主要作用是用作函数形参,通过使用引用变量作为函数参数,函数将使用原始数据,而不是使用数据的副本,这样除了指针之外,为函数访问原始数据提供了另一种快捷的方法,还有,对于设计类来说,引用也是必不可少的。

引用变量的定义

注意:引用变量在定义时必须初始化,否则编辑器就会报错;而且引用是很忠心的,从它被定义为一个变量的引用后,它不能再作为其他变量的引用,这也是引用和指针的区别之一。

int a=0;    //定义一个整形变量a
int &r=a;   //定义一个整型变量的引用r并作为a的引用

在将一个引用定义完成之后,如果你将其他变量(同类型的)再赋值给这个引用,编译是可以通过的,但是这不是将这个引用的目标重新定位了,而是改变了原来变量的值。比如:

int a = 0, b = 1;
int &r = a;
r = b;

本来想让r作为b的引用,可是这样执行完之后就会发现a不再为0而是1了,因为在执行r = b;这句代码时,r已经代表了变量a,那么这段代码便等同于a = b;所以说引用不能被重新定位,是非常“忠心”的。

原则上引用是要在发生在相同类型的条件下的,比如int类型的引用必须指向int类型的变量,要是将int类型的引用初始化为double类型的变量时编译就会不通过,但是如果给int前加const修饰便可以通过编译,但相应的会丢失精度。

double f = 3.14;
const int &r = f;
cout << "r = " << r << endl;
cout << "f = " << f << endl;

这段代码输出的便是r = 3,f = 3.14。

与指针的关系

引用与指针有很多共性,比如在函数传参时可以在函数内访问到原始数据而不是数据的副本(拷贝了一份的数据),其实引用和指针在底层的实现是一样的,但是引用与指针之间还是有一定的区别的。

  1. 引用在定义时必须初始化,而指针可以不初始化,顶多算个野指针。
  2. 引用在定义完成后不能再被重定向,而指针可以。即使想通过各种花式操作来让引用重定向,也是不行的,比如:
    int a = 0, b = 1;
    int *p = &a;    //定义一个指针p先指向a
    int &r = *p;    //定义一个引用r作为p指向的变量a的引用
    p = &b;         //将p重新指向变量b
    cout << "r = " << r << endl;  //输出r的值
    cout << "*p = " << *p << endl;  //输出指针p指向的变量

这段代码输出结果为r = 0;*p = 1;
说明r被定义出来作为a的引用后并不会因为中间经了一手指针p而随着指针p被重定向。
3. 指针在要访问指向的值时需要解引用,而引用不需要(可以理解为引用在使用时编译器隐式的将它解引用了,因为引用和指针在底层实现时其实是一样的)

解析函数参数的三种传递方法

  1. 变量作为形参(值传递)
int cub(int n)      //这个函数接受一个int类型的参数,然后返回这个参数的三次方的值
{
    n *= n * n;
    return n;
}

int main()
{
    int a = 3;
    int cub_a = cub(a);
    cout << "a = " << a << endl;
    cout<< "a^3 = " << cub_a <<endl;
}

这个函数输出为a = 3, a^3 = 27。
在函数中改变了传入的参数的值,但是传入的参数a的值并没有发生变化,说明这种值传递方式传入的是a的副本,并不是a本身。
2.指针作为形参(地址传递)

int cub(int *n)     //这个函数接受一个指向int类型变量的指针,然后返回这个变量的三次方的值
{
    *n *= *n * *n;
    return *n;
}

int main()
{
    int a = 3;
    int cub_a = cub(&a);
    cout << "a = " << a << endl;
    cout << "a^3 = " << cub_a << endl;
    return 0;
}

这个函数输出为a = 27, a^3 = 27。
在函数中改变了传入的参数的值,相应的传入的参数的值也随着改变了,说明传入指针的方式使用的就是原数据本体。

  1. 引用作为形参
int& cub(int &n)        //这个函数接受一个指向int类型变量的引用,然后返回这个变量的三次方的值的引用
{
    n *= n * n;
    return n;
}

int main()
{
    int a = 3;
    int &r = a;
    int &cub_a = cub(r);
    cout << "a = " << a << endl;
    cout << "a^3 = " << cub_a << endl;
    return 0;
}

这个函数输出为a = 27, a^3 = 27。
在函数中改变了传入的参数的值,相应的传入的参数的值也随着改变了,说明传入引用的方式使用的也是原数据本体。

说明在函数参数中传入引用使用的也是原数据本体,这也从一方面验证了引用和指针在底层实现都是保存了原数据的地址来进行操作的,只是编译器在使用指针时进行了隐式的解引用。

在函数传递和返回参数时,特别是一些占内存比较大的结构体(对象),要作为参数传递或返回时应该尽可能地采用引用,这样传入和返回都是一个地址,若采用值传递和值返回,都要将整个结构体(对象)拷贝一份,这样就需要很大的开销。使用引用可以提高程序的运行效率。

在以引用作为参数的函数内不想改变传入参数的值时,应该尽可能地用const来修饰,这样可以防止在函数内对原始数据的误改,如果出现了误改编译器就会报错提示。

引用作为返回值时要注意的

函数返回引用时要避免返回栈上空间的引用

int& fun()  
{
    int a = 0;
    int &r = a;
    return r;
}

这个函数中,定义了一个变量a,这个a是一个局部变量,是在函数栈帧上的变量,返回的r是a的地址,函数结束即销毁,返回的地址也就没有了意义。


参考:《C++ Primer Plus第六版 》 人民邮电出版社

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值