• ”引用 & 指针“ -- 作为函数的参数和返回值小结


    转自:http://wenku.baidu.com/view/3e1957270066f5335a8121cc.html

       http://see.xidian.edu.cn/cpp/biancheng/view/50.html


    一、引用作为函数参数

    作为函数参数时引用有两种原因:
    在函数内部会对此参数进行修改
    提高函数调用和运行效率

    关于第一点,都知道C++里提到函数就会提到形参和实参。如果函数的参数实质就是形参,不过这个形参的作用域只是在函数体内部,
    也就是说实参和形参是两个不同的东西,要想形参代替实参,肯定有一个值的传递。函数调用时,值的传递机制是通过“形参=实参”
    来对形参赋值达到传值目的,产生了一个实参的副本。即使函数内部有对参数的修改,也只是针对形参,也就是那个副本,实参不会
    有任何更改。函数一旦结束,形参生命也宣告终结,做出的修改一样没对任何变量产生影响。

    例如: 

    void swap(int p1, int p2) //对两个变量进行交换处理。此处函数的形参为p1, p2,没有引用
    {int p; p=p1; p1=p2; p2=p; }
    void main( )
    {int a,b;
    cin>>a>>b; //输入a,b两变量的值
    swap(a,b); //直接以变量a和b作为实参调用swap函数
    cout<<a<< ' ' <<b; //输出结果

    你会发现输出的a和b还是你输入的值,没有交换。

    如果我们改为:

    void swap(int &p1, int &p2) //对两个变量进行交换处理。此处函数的形参为p1, p2都是引用
    { int p; p=p1; p1=p2; p2=p; }

    再次执行,就会发现值交换了。

    原理就在于采用&p1和&p2时,p1和p2是实参的别名而已,像一个指针指向实参。改变p1和p2就是改变实参的值。

    关于第二点,可以结合第一点分析,p1和p2是实参的引用,不用经过值的传递机制,已经有了实参值的信息。所以没有了传值和生成副本的时间和空间消耗。当程序对效率要求比较高时,这是非常必要的,比如单片机。所以函数参数时的引用又被形象地称为:“虚实结合”。 

    二、引用作为函数返回值

    这一部分就引用我收集的资料

    说明:

    (1)以引用返回函数值,定义函数时需要在函数名前加&

    (2)用引用返回一个函数值的最大好处是,在内存中不产生被返回值的副本。

    例如:

    #include <iostream.h>
    float temp; //定义全局变量temp
    float fn1(float r); //声明函数fn1
    float &fn2(float r); //声明函数fn2
    float fn1(float r) //定义函数fn1,它以返回值的方法返回函数值
    {
     temp=(float)(r*r*3.14);
     return temp;
    }
    float &fn2(float r) //定义函数fn2,它以引用方式返回函数值
    {
     temp=(float)(r*r*3.14);
     return temp;
    }
    void main() //主函数
    {
     float a=fn1(10.0); //第1种情况,系统生成要返回值的副本(即临时变量)
     float &b=fn1(10.0); //第2种情况,可能会出错(不同 C++系统有不同规定)
     //不能从被调函数中返回一个临时变量或局部变量的引用
     float c=fn2(10.0); //第3种情况,系统不生成返回值的副本
     //可以从被调函数中返回一个全局变量的引用
     float &d=fn2(10.0); //第4种情况,系统不生成返回值的副本
     //可以从被调函数中返回一个全局变量的引用
     cout<<a<<c<<d;
    }

    引用作为返回值,必须遵守以下规则:

    (1)不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。

    (2)不能返回函数内部new分配的内存的引用。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一 个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。

    (3)可以返回类成员的引用,但最好是const。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常 量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。

    引用和指针一样,是C++里面的一个大话题。关于引用的问题还有很多,比如const引用、引用与一些操作符的重载、引用和多态、引用与拷贝构造函数"X(&X)"等等。需要各位自己动手过程发现和解决了。因为和本文主题相关不大,所以就不赘述了。


    指针在函数中的使用也是十分广泛的。某些情况下,将指针作为函数的参数或函数的返回值会给我们带来方便。而某些情况下,我们又不得不将指针作为函数的参数或函数的返回值。

    指针作为参数

    我们在上一章我们已经了解向函数传递数组的实质是向函数传递数组首元素的地址。我们又知道数组名是一个指向数组首元素的指针常量。所以我们认为,向函数传递数组是将指针作为参数的特殊形式。

    由于指针可以直接操作内存中的数据,所以它可以用来修改实参。这个功能和引用是类似的。

    下面我们来看一段程序,了解指针作为参数时的上述两个特点:(程序8.6.1)

    #include "iostream.h"
    void arrayCopy(int *src,int *dest,int size);//复制数组元素
    void display(const int *array,int size);//输出数组元素
    int main()
    {
       int a[]={3,4,5,6,3,1,6};
       int b[7];
       arrayCopy(a,b,sizeof(a)/sizeof(int));//把数组a的元素依次复制到数组b中
       cout <<"The data of array a is:";
       display(a,sizeof(a)/sizeof(int));
       cout <<"The data of array b is:";
       display(b,sizeof(b)/sizeof(int));
       return 0;
    }
    void arrayCopy(int *src,int *dest,int size)
    {
       for (int i=0;i<size;i++)
       {
          dest[i]=src[i];//修改了实参数组元素
       }
       cout <<size <<" data Copied." <<endl;
    }
    void display(const int *array,int size)//const用来保护指针指向的数据
    {
       for (int i=0;i<size;i++)
       {
          cout <<array[i] <<" ";
       }
       cout <<endl;
    }
    
    运行结果:
    7 data Copied.
    The data of array a is:3 4 5 6 3 1 6
    The data of array b is:3 4 5 6 3 1 6


    根据arrayCopy函数,不难看出传递数组和传递指针是完全相同的。而通过指针的间接引用或数组操作,我们可以在函数内实现对实参的修改。这就是arrayCopy函数能够实现复制功能的原因。

    不过,将指针作为函数参数的副作用仍然不容我们忽视。指针和引用虽然都能够修改实参,但是指针却更加危险。因为引用仅限于修改某一个确定的实参,而指针却可以指向内存中的任何一个数据,通过间接引用就能够在一个函数内修改函数外甚至系统中的数据了。这样一来,函数的黑盒特性就被破坏了,系统也因此变得不再安全。对于程序员来说,将指针作为函数参数可能把函数内的问题引到函数外面去,使得调试程序变得非常困难。所以,我们要认识到使用指针的两面性,谨慎对待指针做函数参数。

    为了避免指针作为函数参数导致数据被意外修改,我们可以使用const来保护指针指向的数据,如程序8.6.1中的display函数。

    指针作为返回值

    和别的数据类型一样,指针也能够作为函数的一种返回值类型。我们把返回指针的函数称为指针函数。在某些情况下,函数返回指针可以给我们设计程序带来方便。而且此时通过间接引用,函数的返回值还可以作为左值。

    下面我们来看一段程序,了解函数如何返回指针:(程序8.6.2)

    #include "iostream.h"
    int * max(int *array,int size);//返回值类型是int *,即整型指针
    int main()
    {
       int array[]={5,3,6,7,2,1,9,10};
       cout <<"The Max Number is " <<*max(array,sizeof(array)/sizeof(int)) <<endl;//间接引用返回的指针
       return 0;
    }
    int * max(int *array,int size)//寻找最大值
    {
       int *max=array;
       for (int i=0;i<size;i++)
       {
          if (array[i]>*max)
          max=&array[i];//记录最大值的地址
       }
       return max;
    }
    
    运行结果:
    The Max Number is 10


    需要注意的是,返回的指针所指向的数据不能够是函数内声明的变量。道理很简单,我们在第六章已经说明,一个函数一旦运行结束,在函数内声明的变量就会消失。就好像下课同学们都走了,教室里的某一个座位到底有没有坐着谁我们无法确定。所以指针函数必须返回一个函数结束运行后仍然有效的地址值。

  • 相关阅读:
    只需 4 步,手把手教你如何实现滤镜功能
    paip.提升用户体验---c++ qt自定义窗体(1)---标题栏的绘制
    socket网络编程的一些基础知识
    图像处理、模式识别、模式分类、机器视觉推荐图书
    数据挖掘、机器学习和模式识别关系与区别
    Qt4_VS10 程序打包发布
    Qt之多线程
    QT中.pro文件的写法
    高斯定理的证明
    《Linear Algebra and Its Applications》-chaper3-行列式-行列式初等变换
  • 原文地址:https://www.cnblogs.com/zhangxiaosong/p/3274209.html
Copyright © 2020-2023  润新知