C++初级主题--(4)引用

来源:互联网 发布:angular.js介绍 编辑:程序博客网 时间:2024/05/20 05:04

一.引用的概念

C++函数中参数的传递方式是传值。在函数域中为参数重新分配内存,而把实参的数值传递到新分配的内存中。它的优点是有效避免函数的副作用(即改变实参的值)。
如果要求改变实参的值,怎么办呢?如果实参是一个复杂的对象,重新分配内存会引起程序执行效率大大下降,怎么办呢?在C++中有一种新的导出型数据类型—引用(reference)可以解决上面的难题。引用又称别名(alias)。

二.传值和传引用的区别

1.用交换程序举例说明
(1)直接通过传值的方式无法更改实参的值

#include<iostream>using namespace std;void swap1(int x, int y){    int tmp = y;    y = x;    x = tmp;}void main(){    int a = 10;    int b = 20;    swap1(a,b);}

a,b的值仍未交换,只交换了x,y的值。
这里写图片描述
(2)C语言中通过指针的方式可以交换

#include<iostream>using namespace std;void swap1(int *x, int *y){    int tmp = *y;    *y = *x;    *x = tmp;}void main(){    int a = 10;    int b = 20;    swap1(&a,&b);}

可以看到x,y与a,b的地址不同,额外为形参开辟了空间
这里写图片描述
(3)C++中提供引用的方式

#include<iostream>using namespace std;void swap1(int &x, int &y){    int tmp = y;    y = x;    x = tmp;}void main(){    int a = 10;    int b = 20;    swap1(a,b);}

可以看到x,y的地址与a,b相同,是a,b的别名,没有重新分配空间。
这里写图片描述

2.总结
(1)传引用可以直接更改实参的值
(2)传引用不用额外分配空间保存实参的数值。
(3)不能定义空引用,即引用的对象必须存在。
3.引用的本质仍是指针
关于这一点,请参考博文 c++ 引用 底层实现机制

  1. 引用是在编译的过程中被处理的,实际上就是在编译层面对程序员进行的一个比较友好的语法,而在实现上是由编译器完成了地址的传递,实质上还是指针。
  2. 不能简单的理解为一个别名,我们可以这样用,但是要知道底层就是一个指针变量,是要占用内存空间的,和define是不一样的。

三.引用的分类

1.变量的引用

int a = 10;int b = &a;

b是a的引用

2.指针的引用

int a = 10;int *p = &a;int *&q = p;

q是p的引用

3.数组的引用

int ar[10] = {0};int (&br)[10] = ar;

br是ar的引用

4.常量的引用
(1)常量必须用常量引用

const int x = 100;const int &y = x;

(2)变量可以用常量引用

int x = 100;const int &y = x;

(3)不同类型间进行常引用

double x = 13.14;const int &y = x;

这里写图片描述可以看到y与x的地址不同,此时,不同类型之间进行赋值运算,一定会产生临时对象(假设为tmp),最终y是tmp的引用。同时,应注意:对于所有的临时对象,必须同样假设它们是不可存取的,即具有常量的性质。当改变这种数据时,编译器会指出错误,这是非常有用的提示,因为这个改变会导致信息丢失。

5.函数中的引用,即函数返回引用或函数参数中包含引用

(1)最经常看见引用的地方是在函数参数和返回值中。当引用被用作函数参数时,在函数内任何对引用的更改将对函数外的参数产生改变。
(2)当然可以传递一个指针来做相同的事情,但引用具有更清晰的语法。(可以把引用看作一个使语法更加便利的工具。)
(3)如果函数返回一个引用,必须像从函数返回一个指针一样对待。当函数返回时,无论引用关联的是什么都应该存在,否则,将不知道指向哪一个内存

int* fun1(int *x)    //(2){    (*x)++;    return x;    //正确,x指向确定的内存} int& fun2(int &x)    //(1){    x++;    return x;    //正确,x指向确定的内存}int& fun3(){    int q;    //return q;    //错误,局部变量,最终指向不明确的内存    static int p;    return p;    //正确,尽管fun3运行结束,但是p被static修饰,为全局变量,生存作用域仍存在,指向明确的内存}int main(){    int a = 10;    fun1(&a);   //ugly(but explicit)    fun2(a);    //clean(but hidden)  }

四.参数传递准则

当给函数传递参数时,人们习惯上通过常量引用来传递。虽然看起来似乎仅是出于效率考虑(通常在设计与装配程序时并不考虑效率),但是这样会带来很多危险。拷贝构造函数需要通过传值方式传递对象,但并不总是可行的
这种简单习惯可以大大提高效率:传值方式会调用构造函数和析构函数,但是如果不想改变参数,则可以通过常量引用传递,它仅需要将地址压栈。

事实上,只有一种情况不适合用传地址方式。就是当传值是唯一安全的途径,否则将会破坏对象时。所以需要依据上下文

1 0
原创粉丝点击