传值还是传引用(2) — C++

来源:互联网 发布:软件随想录 txt 编辑:程序博客网 时间:2024/06/05 09:54

上一篇文章总结了java 参数传递方式。—(http://blog.csdn.net/clam_clam/article/details/6625837)
为了比较我在这里总结了C++ 的参数传递方式。
首先来看一个小程序:

#include <iostream>using namespace std;class Interger{public:int value;Interger(int b){this->value=b;}};void swap(int a,int b){int temp=a;a=b;b=temp;}void swapByReference(int &a,int &b){int temp=a;a=b;b=temp;}void swap(Interger IntergerA, Interger IntergerB){Interger IntergerTemp=IntergerA;IntergerA=IntergerB;IntergerB=IntergerTemp;}void changeInteger(Interger IntergerA){IntergerA.value=100;}void main(){int inta=5;int intb=6;cout<<"before call swap by value a : "<<inta<<"  ,inta:  "<<intb<<endl;swap(inta,intb);cout<<"after call swap by value a:  "<<inta<<"  ,intb:  "<<intb<<endl;cout<<"before call swap by reference  a:  "<<inta<<" ,b:  "<<intb<<endl;swapByReference(inta, intb);cout<<"after call swap by reference a:  "<<inta<<" ,b:  "<<intb<<endl;Interger IntergerA=Interger(8);Interger IntergerB=Interger(9);cout<<"before call swapInterger  IntergerA: "<<IntergerA.value<<"  ,IntergerB:  "<<IntergerB.value<<endl;swap(IntergerA,IntergerB);cout<<"after call swapInterger  IntergerA: "<<IntergerA.value<<"  ,IntergerB:   "<<IntergerB.value<<endl;cout<<"before call changeInteger  IntergerA  value:  "<<IntergerA.value<<endl;changeInteger(IntergerA);cout<<"after call changeInteger  IntergerA  value:  "<<IntergerA.value<<endl;}

运行结果:
before call swap by value a : 5  ,inta:  6
after call swap by value a:  5  ,intb:  6
before call swap by reference  a:  5 ,b:  6
after call swap by reference a:  6 ,b:  5
before call swapInterger  IntergerA: 8  ,IntergerB:  9
after call swapInterger  IntergerA: 8  ,IntergerB:   9
before call changeInteger  IntergerA  value:  8
after call changeInteger  IntergerA  value:  8
Press any key to continue

可以看出:
1. 只有传递引用时,swap (&a,&b),实参才改变了。
2.而对于void swap(Interger IntergerA, Interger IntergerB),没有改变实参值。
3. 并且调用void changeInteger(Interger IntergerA) 前后 IntergerA.value 的值也没有改变。
所以对C++ 不管是基本类型还是自定义类型都是传值(特别要注意C++这里自定义对象传值是传递自定义对象的各成员值,而不是像java(引用类型)那样把实参的地址传值给形参,如果是把实参地址传给形参那changeInteger 后 IntergerA.value 的值应该会变。而程序运行结果显示没有变还是8)。 

        java 参数传递形式有只有一种 传值 只不过有传值方式:1.传值的副本(基本对象) 2. 传地址的副本(引用型对象)。
C++ 参数传递就有两种1.传值,2.传引用 如上面的void swapByReference(int &a,int &b) 。

        引用可以看作是一个变量的别名,使用 引用 时 ,对于void swapByReference(int a,int b)   编译器并没有给形参a,b分配新的内存空间,只是使形参a,b指向了main函数中实参a,b的内存空间,他们共享同一内空间,即把地址给了形参。
       
      指针方式和引用方式都属于传址调用。那么指针和引用又有什么样的区别呢?
     两种参数都允许函数修改实参所对应的对象,两种类型的参数都允许有效得向函数传递大型类对象。
两者在参数传递过程中,有如下几点不同
   (1)引用必须被初始化为指向一个对象,一旦初始化了,它就不能在指向其它对象指针可以指向一系列不同的对象,当然也可以定义为NULL
   如:
   
 calss Type{   void operation(const Type&p1,const Type&p2);   int main(){    Tyoe obj1;    Type obj2 = operation(obj1,0);    //引用参数的实参不能为0   }
所以在函数中,一个参数可能指向不同的对象的情况,或者这个参数可能不指向任何对象,则必须实用指针参数。

       (2)引用参数的一个重要用法,它允许我们在有效实现重载操作符的还能保证用法的直观性。如下例:

示例 Matrix-1:

 Matrix operator+(Matrix m1,Matrix m2)   {    Matrix result;    //do computation    return result;   }
通过上面实现后,就能够支持两个Matrix对象的加法,如:a+b
   但是这样做,效率会非常低。因为该实现的实参是按值传递,两个Matrix对象相加的因为Matrix对象非常大的时候,分配这样一个对象,并把它拷贝到函数参数区中的时间和空间开销比较高。时候,内容被拷贝到operator+()函数的参数区中,

   而为了提高我们的操作符函数的效率,假定我们决定把参数申明为指针的时候,如下:

示例 Matrix-2:

Matrix operator+(Matrix *m1,Matrix *m2)   {    Matrix result;    //do computation    return result;   }

 这种做法,在一定程度上很好得解决了函数实现的效率问题,但是带来一个新的问题是用户的使用习惯,对于这样的operator+操作,调用方式变为:&a+&b,这样大大颠覆了我们传统的调用方式。
   所以这时候,如果申明为引用的方式,就能到达到效率和使用习惯的目的:

示例 Matrix-3:

Matrix operator+(Matrix &m1,Matrix &m2)   {    Matrix result;    //do computation    return result;   }
调用的时候还经以前一样(使用习惯一样):
如我们刚才是这样调用的:swapByReference(inta, intb); 。

再来看一个例子:充分理解 C++ 3种传参方式:

1.将变量名作为形参和实参
在这种情况下传给形参的是变量的值。传递是单向的,即如果在执行函数期间形参的值发生变化,并不传回给实参,这就是值传递方式。因为在调用函数期间,形参和实参不是同一个存储单元。
int main(){    void swap(int,int);//参数为整型变量    int i=3,j=4;    cout<<"i="<<i<<",j="<<j<<endl;    swap(i,j);//变量名    cout<<"i="<<i<<",j="<<j<<endl;    system("PAUSE");    return 0;} void swap(int a,int b){//形参为整型变量     int temp;     temp=a;     a=b;     b=temp;}


结果:
i=3,j=4
i=3,j=4
可以发现,执行函数swap后,形参a和b的改变不会影响实参i和j的值。
2.传递变量指针
形参是指针变量,实参是一个变量的地址,调用函数时,形参(指针变量)指向实参变量单元。
int main(){    void swap(int*,int*);//参数为整型指针变量    int i=3,j=4;    cout<<"i="<<i<<",j="<<j<<endl;    swap(&i,&j);//变量地址    cout<<"i="<<i<<",j="<<j<<endl;    system("PAUSE");    return 0;} void swap(int *p1,int *p2){//形参为整型指针变量     int temp;     temp=*p1;     *p1=*p2;     *p2=temp;}

结果:
i=3,j=4
i=4,j=3
调用函数时把变量i和j的地址传送给形参p1和p2,因此*p1和i为同一内存单元,*p2和j是同一内存单元。
这种方式还是“值传递”,只不过实参的值是变量的地址而已。而在函数中改变的不是实参的值(即地址,这种改变也影响不到实参),而是实参地址所指向的变量的值。
3.“引用形参”
int main(){    void swap(int&,int&);//参数为整型变量的引用    int i=3,j=4;    cout<<"i="<<i<<",j="<<j<<endl;    swap(i,j);//变量名    cout<<"i="<<i<<",j="<<j<<endl;    system("PAUSE");    return 0;} void swap(int &a,int &b){//形参为引用类型     int temp;     temp=a;     a=b;     b=temp;}

结果:
i=3,j=4
i=4,j=3
当main函数调用swap函数时,由实参把变量名传给形参。i的名字传给引用变量a,j的名字传给引用变量b。此时a和b就分别与i,j占用同一内存空间。这种把实参地址传递到形参,使形参的地址取实参的地址,从而使形参与实参共享同一单元的方式,就是地址传递方式
这里要说明的是,
[1]方式2传递指针变量要另外开辟内存单元(地址副本),其内容为地址;而方式3引用不是一个独立的变量,不单独占内存单元
[2]方式3中,main函数调用swap函数时,实参不必用函数的地址(即&i,&j) ,而直接使用变量名(保持了用户习惯)。系统向形参传递的是实参的地址而不是实参的值。

对于方式—2 传递变量指针。
【调用函数时把变量i和j的地址传送给形参p1和p2,因此*p1和i为同一内存单元,*p2和j是同一内存单元。
这种方式还是“值传递”,只不过实参的值是变量的地址而已。而在函数中改变的不是实参的值(即地址,这种改变也影响不到实参),而是实参地址所指向的变量的值。】

我们在实践一下。因为代码里使用的是:
void swap(int *p1,int *p2){//形参为整型指针变量     int temp;     temp=*p1;     *p1=*p2;     *p2=temp;}
大家可以看到使用的是  temp=*p1,*p1=*p2 。这样我们改变的是实参地址所指向的变量指
如果我们稍微改一下代码就会产生我在《传值还是传引用(1)—java》 一文一样的例子。

如下代码:
int main(){    void swap(int*,int*);//参数为整型指针变量    int i=3,j=4;    cout<<"i="<<i<<",j="<<j<<endl;    swap(&i,&j);//变量地址    cout<<"i="<<i<<",j="<<j<<endl;    system("PAUSE");    return 0;}void swap(int *p1,int *p2){//形参为整型指针变量int *temp;temp=p1;p1=p2;p2=temp;}
运行结果:
i=3,j=4
i=3,j=4
请按任意键继续. . .
这个意思和结果与《传值还是传引用(1)—java》 一文(http://blog.csdn.net/clam_clam/article/details/6625837)
的这个例子是一个例子。都是只改变了交换了形参的值,而没有改变形参和实参所指向的共同地址的变量的值。
public   static   void   swap(Integer   ia,   Integer   ib) { Integer   temp   =   ia;   ia   =   ib;   ib   =   temp;   } 
看到没这两段代码都是交换形参的地址。没有影响到实参。大家可以自己拷贝到电脑上运行一下就知道了(我已经亲身测验过)。

本人才疏学浅,大家如有异议,欢迎评论。
note: 指针,引用做形参传递递参数无论是在效率上还是在空间上都是完全一样的。可以参考博客: http://blog.csdn.net/kandypig/article/details/1587221 


原创粉丝点击