传值调用与引用调用

来源:互联网 发布:php 整数求余数 编辑:程序博客网 时间:2024/05/29 05:13

C++的两种调用方式:传值调用和引用调用  

一、函数的参数传递及传值调用

函数名(参数表)    将实参的值按位置传递给对应的形参变量

1、形参出现在函数定义中,在整个函数体内都可以使用, 离开该函数则不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。

2、实参可以是常量、具有值的变量或表达式,形参只能是变量名。

3、当实参和形参的类型不匹配时,编译器将实参转化为与形参一致的类型后再赋给形参。见example 1

//example 1:求 x的n 次幂#include "stdafx.h"#include <iostream>using namespace std;float power(float x,int n){float pow=1;while(n--)pow*=x;return pow;}int _tmain(int argc, _TCHAR* argv[]){int n=3;float x=4.6;char c='a';cout<<"power("<<x<<','<<n<<")="<<power(x,n)<<endl;cout<<"power("<<c<<','<<n<<")="<<power(c,n)<<endl;cout<<"power("<<n<<','<<x<<")="<<power(n,x)<<endl;system("pause");return 0;}

4、传值调用,将实参的值复制给形参,在函数中参加运算的是形参,而实参不会发生任何改变。起到一种隔离作用

example2 中strcpy(s3,s2)实现字符数组s2[]的内容复制到字符数组s3[]中。复制给形参的是实参数组的首地址,而参加运算的是实参数组。

//example2char s2[10],s3[20];strcpy(s3,s2);

二、引用与复制构造函数

传值调用不会修改实参的值,在函数域中为参数重新分配内存。若实参为一个复杂的对象,重新分配内存会引起程序执行效率大大下降。

引用:为一个已经定义的变量重新起一个别名,即不用为引用类型变量分配内存空间。

        类型  &  引用变量名 = 已定义过的变量名

引用可以作为函数的返回值。一般函数返回一个值时,要生成一个临时变量作为返回值的副本,而用引用作为返回值时,不生成值的副本,效率提高了。见example 3

//example 3#include <iostream>using namespace std;double temp;double fsqr1(double a){temp=a*a;return temp;}double &fsqr2(double a){temp=a*a;return temp;}int main(){double x=fsqr1(5.5);double x=fsqr2(5.5);return 0;}


         

需要注意的地方:

1、采用引用返回方式时,返回的不能是函数中的局部变量,因为这时返回的局部变量地址已经失效。引用方式返回最常用的是由引用参数传递过来的变量(在主函数中已声明的变量),其次是全局变量,这样返回的变量地址是有效的。

2、对于数组,只能引用数组元素,不能引用数组(数组名本身为地址)。

复制构造函数:建立对象时用同一类的另一个对象来初始化该对象。一般对象产生时都会触发构造函数的执行,但是在产生对象的副本时却不会这样,这时执行的是对象的复制构造函数。

解释:一般的构造函数都是会完成一些成员属性初始化的工作,在对象传递给某一函数之前,对象的一些属性可能已经被改变了,如果在产生对象副本的时候再执行对象的构造函数,那么这个对象的属性又再恢复到原始状态,这并不是我们想要的。所以在产生对象副本的时候,构造函数不会被执行,被执行的是一个默认的构造函数。当函数执行完毕要返回的时候,对象副本会执行析构函数,如果你的析构函数是空的话,就不会发生什么问题,但一般的析构函数都是要完成一些清理工作,如释放指针所指向的内存空间。这时候问题就可能要出现了。构造函数不仅完成数据成员的初始化,它还应该进行类对象所需的动态资源的分配。
假如你在构造函数里面为一个指针变量分配了内存,在析构函数里面释放分配给这个指针所指向的内存空间,那么在把对象传递给函数至函数结束返回这一过程会发生什么事情呢?首先有一个对象的副本产生了,这个副本也有一个指针,它和原始对象的指针是指向同块内存空间的。函数返回时,对象的析构函数被执行了,即释放了对象副本里面指针所指向的内存空间,但是这个内存空间对原始对象还是有用的啊,就程序本身而言,这是一个严重的错误。然而错误还没结束,当原始对象也被销毁的时候,析构函数再次执行,对同一块系统动态分配的内存空间释放两次是一个未知的操作,将会产生严重的错误。

解决方案:利用复制构造函数来解决这一问题。复制构造函数就是在产生对象副本的时候执行的,我们可以定义自己的复制构造函数。在复制构造函数里面我们申请一个新的内存空间来保存构造函数里面的那个指针所指向的内容。这样在执行对象副本的析构函数时,释放的就是复制构造函数里面所申请的那个内存空间。


定义一个商品类及其复制构造函数

//example 4:CGoods class#include "stdafx.h"#include "string.h"#include <iostream>#include <iomanip>#include <cstring>using namespace std;class CGoods{private:char Name[21];int Amount;float Price;float Total_value;public:        CGoods();CGoods(CGoods &);//copy constructorCGoods(char[],int,float);};CGoods::CGoods(){Name[0]='\0';Price=0.0;Amount=0;Total_value=0.0;}CGoods::CGoods(CGoods &cgd){strcpy(Name,cgd.Name);Price=cgd.Price;Amount=cgd.Amount;Total_value=cgd.Total_value;}CGoods::CGoods(char name[],int amount,float price){strcpy(Name,name);Amount=amount;Price=price;Total_value=price*amount;}

需要注意的地方:

1、复制构造函数的参数(同类的对象)采用引用方式。如果把一个真实的类对象作为参数传递到复制构造参数时,此时为传值调用,则会在函数中重新分配内存以建立与参数同类型的变量 或对象(编译器会自动为这个对象调用复制构造参数,可参见图1),再将参数的数据成员赋给新的变量或对象。显然这将会引起新一轮的调用,引起无穷递归。

2、当成员函数的参数为同一类的对象或它的引用,在函数体内使用参数对象的私有成员时,可用点号访问操作符,而不必通过接口。

在C++中,下面三种对象需要拷贝的情况。因此,复制构造函数将会被调用。
1)一个对象以值传递的方式传入函数体(函数的形参是类的对象
2)一个对象以值传递的方式从函数返回(函数的返回值是类对象,要建立一个临时对象作为载体,再返回调用者。)
3)一个对象需要通过另外一个对象进行初始化



 

0 0