现在的位置: 首页 > 综合 > 正文

学习心得——参数传递

2018年05月05日 ⁄ 综合 ⁄ 共 2597字 ⁄ 字号 评论关闭

           函数的参数传递(parameter passing)有三种类型:实参值传递(argument value passing)、指针传递(pointer passing)和引用传递(reference passing)。其中实参值传递和指针传递本质上是相同的,传递的是变量的。引用传递的本质是传递地址,故称为地址传递。(这些是我自己归纳的,不是很准确,关键在于理解)

 

一、实参值传递

        所谓实参值传递,就是变量名作为函数的实参(argument)和形参(parameter)。

        当函数调用的时候,实参的值被复制,然后赋给形参。也就是说,形参是实参的一个副本(copy)。

        这个复制的过程是由形参所属的数据类型的复制构造函数(copy constructor)完成的,在函数运行结束后,形参所属的数据类型的析构函数负责释放该形参。

        形参作为一个局部变量,它的作用域在函数内部,一旦跳出了函数,形参的值不被保存,这样在函数体内就不能改变实参的值。当函数返回时,形参的值不会被复制到对应的实参中去。

        整个传递过程是单向的。在调用函数时,形参和实参处于不同的存储单元。

        这种传递方式非常简单,不再举例。

        分析这种传递方式会发现它有几个缺点:

        (1)函数内无法改变实参的值。因为传递的是值,值是赋给形参的,编译的时候形参其实是作为一个临时变量,它被初始化为实参的值。即形参和实参是两个变量,它们之间基本没有关系。对形参做任何修改不会影响到实参。

        (2)增加程序的运行开销。假如实参是一个很大的对象(你可以把它想象成一个很大的数组,虽然数组没有这种传递方式,传递的过程就是复制数组的每一个元素),那么将其复制到形参需要花费大量的时间,占用较多的内存,而在函数调用结束后,析构函数又需要很长的时间来释放空间。

        (3)有些对象无法被复制。例如某些类类型。

 

二、指针传递

        指针传递,顾名思义,指针变量作为函数的参数。如下代码所示:

void reset(int *ip)  //指向整型的指针变量做函数参数
{
*ip=0;           //改变了指针变量指向的对象的值
ip=0;            //改变了指针变量的值
}
int main()
{
int i=42;                //定义一个整型变量
int *p=&i;               //定义一个指针变量p,指向整型变量i
cout<<i<<" "<<*p<<endl;  //输出变量和指针变量指向的变量的值,结果为:42 42
reset(p);                //调用reset函数
cout<<i<<" "<<*p<<endl;  //输出变量和指针变量指向的变量的值,结果为:0 0
return 0;
}

        分析上面的代码,在reset函数中,我们使用*ip=0将指针变量指向的对象的值改为0,我们使用ip=0将指针变量的值改为0。第一条语句改变了变量i的值,第二条语句改变的是局部变量ip,所以最后i的值仍为0。

        这个过程中,形参是指针变量,实参是变量的地址,函数被调用时,实参的地址被复制,然后赋值给形参。形参指向实参变量单元,一旦形参发生变化,实参也随之变化。这种方式叫虚实结合,其本质仍然是值传递。虽然形参是指针变量,实参是变量的地址,但是我们看到,当我们使用ip=0修改指针变量的值以后,实参的值并没有改变。也就是说,我们只是通过形参来访问实参,修改实参的值,而修改形参的地址,不会对实参产生任何影响。

        好了,现在我们将指针传递与实参值传递进行对比:

        相同点:都是值传递,传递的过程都是复制的过程。

        不同点:实参值传递复制的是实参的值,指针传递复制的是实参的地址;实参传递方式在函数内无法修改实参值,指针传递在函数内可以通过地址访问到实参,进而修改实参值。

        总结:指针传递相比于实参传递,更加灵活,而且它没有实参传递的两个缺点(当需要传递一个大的对象时,我们可以只传递它的首地址),所以这种传递方式被广泛使用。最常见的就是数组作为参数。       

        由于指针传递比较灵活,我们可以对其进行限定,限定方式主要是添加const关键字。例如我们想防止实参被修改,那么我们可以用一个常变量指针作为参数,void reset(const int *ip),这样就只能获得实参值,而无法修改实参。

 

三、引用传递

        引用传递,就是以变量的引用作为函数的参数。例如下面的代码:

void reset(int &ip)  //整型变量的引用做函数参数
{
ip=0;             //改变了指针变量的值
}
int main()
{
int i=42; //定义一个整型变量 
reset(i); //调用reset函数 
cout<<i<<endl; //输出结果为0 
return 0;
}

           上述代码中,reset函数中,形参为整型变量的应用。在main函数中调用reset函数时,实参是一个整型变量。当在reset函数中修改形参时,实参随之变化。在这个过程中,传递的是地址,形参作为一个引用变量,直接指向了实参的地址,而不是复制。

        引用传递和指针传递类似,但是引用传递不是复制地址,而是直接指向地址。所以对形参的任何修改都会影响到实参,这使得它使用起来很容易出错。在实际使用的过程中,我们经常使用常引用变量作为形参。如void reset(const int &ip),这样我们就不能修改形参的值,从而导致实参的值的改变。

        如果我们是用非常量引用作为形参,使用起来有很多限制:(1)实参类型必须与形参完全一样,不能有任何类型转换;(2)实参不能是常量;(3)实参必须是左值。

 

总结:

        本文介绍了函数参数的几种形式,先将使用方法归纳如下:

        实参值传递:形参是变量,实参是变量          void reset(int);            reset(i);

        指针传递:    形参是指针,实参是变量地址  void reset(int *);          reset(&i);

        引用传递:    形参是引用,实参是变量          void reset(int &);         reset(i);

 

The End

 

抱歉!评论已关闭.