引用参数与引用返回值 类的拷贝构造

引用地址  http://www.cnblogs.com/bigshow/archive/2008/11/10/1330514.html

经常看到这样的声明:T& func(T& t),这种声明和T func(T t)有什么区别?书上的解释是为了提高效率,究竟是如何提高效率的呢?内部执行了什么操作?本文通过8个小例子对引用参数和引用返回进行了一次彻底的排查。
    首先看一下在类的成员函数中的引用参数和引用返回值:

类定义class A
{
     public:
      int x;

A(){}//构造函数
      A(const A& other)//拷贝构造函数
      {
            this->x = other.x;
            cout << "Copy" << endl;
      }

~A(){}//析构函数

A& operator=(const A& other)//赋值函数
      {
            this->x = other.x;
            cout << "Assign" << endl;
            return *this;
      }

void func1(A a)
      {

}

void func2(A& a)
      {

}

A func3()
      {
            return *this;
      }
 
      A& func4()
      {
            return *this;
      }
};

这个类很简单,只有一个成员变量x,并且定义了默认构造函数、拷贝构造函数、析构函数和赋值函数。为了能够更清楚地看到哪个拷贝构造函数与赋值函数是否被调用,在这两个函数中添加了一些输出信息。
    类中还定义了四个成员函数,下面分别分析这四个函数的执行情况。
    (1) 在main()函数中调用func1():

调用func1()int main()
{
      A a1, a2;
      a2.func1(a1);

return 0;
}

func1()输出结果Copy

为什么会有这样的输出结果呢?这是由于func1()中传递的是值参数,因此在执行函数体之前会先产生一个临时对象,然后调用类的拷贝构造函数初始化这个临时对象,从而输出了"Copy"。在函数内部操作的是这个临时对象,对临时对象所做的任何修改不会反映到函数的实参上。

    (2) 在main()函数中调用func2()以与func1()对比:

调用func2()int main()
{
      A a1, a2;
      a2.func2(a1);

return 0;
}

func2()输出结果

结果什么也没有输出。
    这是由于传入的是一个引用参数,因此在函数内部不需要产生一个临时对象来保存对象信息,因此不会调用拷贝构造函数。这就是引用参数的作用,减少一次对象的拷贝,提高了函数的效率。

(3) 在main()函数中调用func3():

调用func3()int main()
{
      A a1, a2;
      a2 = a1.func3();

return 0;
}

func3()输出结果

Copy
Assign

为什么会输出"Copy"呢?这是因为函数采用的是值返回,因此为了保存返回值,需要先创建一个临时对象,然后调用类的拷贝构造函数将*this的内容拷贝到这个临时对象中,再将临时对象返回。最后通过赋值函数将该临时对象的内容赋值给新对象。

 (4) 在main()函数中调用func4()以与func3()对比:

调用func4()int main()
{
 A a1, a2;
 a2 = a1.func4();

return 0;
}

func4()输出结果

Assign

只调用了赋值函数,这是引用函数采用的是引用返回,因此直接返回对象自身的引用*this,不需要创建临时对象来保存对象信息,因此不会调用拷贝构造函数。最后通过赋值函数直接将对象本身的内容赋值给新对象。这就是引用返回值的作用,减少了一次对象的拷贝,提高了函数的效率。

总结一下:在类的成员函数中,使用引用参数和引用返回值都不需要产生临时对象,减少了一次对象的拷贝,提高了函数的效率。

那么,如果将参数作为返回值返回,并且用引用接收返回值将会产生什么效果呢?下面定义四个全局函数:

全局函数A& func5(A& a)
{
      return a;
}

A& func6(A a)
{
      return a;
}

A func7(A& a)
{
      return a;
}

A func8(A a)
{
      return a;
}

 (5) 在main()函数中调用func5():

调用func5()int main()
{
      A a1;
      a1.x = 1;
      A& a2 = func5(a1);
      a1.x++;
 
      cout << a1.x << endl;
      cout << a2.x << endl;

return 0;
}

func5()输出结果

2
2

func5()采用了引用参数,并且以引用返回值的方式返回了该参数,因此a2是a1的一个引用,对a1的任何改变都会反映到a2上,所以a1、a2的成员变量x的值相同。

    (6) 在main()函数中调用func6():

调用func6()int main()
{
      A a1;
      a1.x = 1;
      A& a2 = func6(a1);
      a1.x++;
 
      cout << a1.x << endl;
      cout << a2.x << endl;
 
      return 0;
}

编译的时候会报一个警告:

警告

warning C4172: returning address of local variable or temporary

func6()输出结果

Copy
2
4198610

警告的意思就是返回了一个局部变量的引用,这种用法实际上是错误的。局部变量在函数返回前就会被释放,因此实际上a2引用到的一块不可知的内存,这从输出的a2.x的值"4198610"也可以看出来。至于输出"Copy",是因为采用的是值参数,上面已经讨论过,这里不再赘述。

 (7) 在main()函数中调用func7():

调用func7()int main()
{
      A a1;
      a1.x = 1;
      const A& a2 = func7(a1);
      a1.x++;
 
      cout << a1.x << endl;
      cout << a2.x << endl;

return 0;
}

func7()输出结果

Copy
2
1

这是一种比较特殊的用法,由于func7()采用的是值返回,因此在函数返回前将会产生一个临时对象,并执行一次拷贝构造函数。这样相当于a2引用了一个临时对象。前面曾经说过,临时对象将会在函数返回前被释放,但是为什么这里输出的结果是正常的呢?这是一种特殊情况,C++规定,如果有临时对象有一个引用,那么这个临时对象的生存期将延长到和这个引用相同。这样就可以解释上面的输出结果了:a2引用了一个临时对象,而不是引用了a1,因此a1的任何改变不会影响到a2。

注意:在VC编译环境下,const A& a2 = func7(a1);这行语句前面可以不加"const",但是在g++或者其他版本的编译器中不加"const"将会产生编译错误。加上"const"更加符合C++标准的规定,因为临时对象不可见,不允许通过该引用来改变临时对象的内容。

  (8) 在main()函数中调用func8():

调用func8()int main()
{
      A a1;
      a1.x = 1;
      const A& a2 = func8(a1);
      a1.x++;
 
      cout << a1.x << endl;
      cout << a2.x << endl;
 
      return 0;
}func8()输出结果

Copy
Copy
2
1

通过以上的分析,对这个输出结果也就很好理解了:由于采用的是值参数,因此在函数体执行前会调用一次拷贝构造函数;采用的是值返回值,因此在函数返回前又会调用一次拷贝构造函数,这就是前两个"Copy"的由来。另外,a2引用的是一个临时对象,而不是引用了a1,因此a1的任何改变不会影响到a2。

总结一下:
    如果使用引用接收引用返回值,则返回的引用必须具有较长的生存期,不可以引用局部变量。
    如果使用引用接收值返回值,则引用了一个临时对象,该对象的生存期将延长到和这个引用相
同。

时间: 2024-10-10 06:20:03

引用参数与引用返回值 类的拷贝构造的相关文章

传引用作为形参和返回值_节省参数拷贝的时间

基本的传引用概念 #include <bits/stdc++.h> using namespace std; //说出以下两个函数的区别 int& whatf(int &a,int &b) //如果不希望函数执行过程中 某变量被修改 用const限定 { a=3;b=2; return a; } int whath(int a,int b) { a=3;b=2; return a; } int main() { int a,b; whatf(a,b)++; cout&l

工作随笔——Java调用Groovy类的方法、传递参数和获取返回值

接触Groovy也快一年了,一直在尝试怎么将Groovy引用到日常工作中来.最近在做一个功能的时候,花了点时间重新看了下Java怎么调用Groovy的方法.传递参数和获取返回值. 示例Groovy代码如下: # TestGroovy.groovy 定义testC方法,传入3个参数,返回处理后的数据 def testC(int numA, int numB, int numC) { "传入参数:" + numA + numB + numC + "计算之和为:" + (

Golang中interface{}作为函数参数和函数返回值的使用

package main import (     "errors"     "fmt" ) type item struct {     Name string } func (i item) String() string {     return fmt.Sprintf("item name: %v", i.Name) } type person struct {     Name string     Sex  string } func

httpclient4.x调用cxf发布的webservice的某个方法(有参数,有返回值)(未整合spring)

原文:httpclient4.x调用cxf发布的webservice的某个方法(有参数,有返回值)(未整合spring) 源代码下载地址:httpclient4.x调用cxf发布的webservice的某个方法(有参数,有返回值)(未整合spring) 采用spring + cxf编写服务端 httpclient4编写客户端调用 如题,代码没有jar 完整包: 链接:http://pan.baidu.com/share/link?shareid=2162612373&uk=402880896 密

PHP_零基础学php_3PHP函数、传参函数、默认参数、函数返回值

<?php function say_hello() //无参数 { $name="tang"; echo "hello,".$name; echo "<br />"; echo "<hr />"; } say_hello();//函数调用 function say_helloS($some_name)//有参数 { echo "hello,".$some_name; echo

为什么类的拷贝构造参数加引用、重载赋值函数的返回值和参数加引用

class string { public: string(const char *str=NULL); string(const string& str);     //copy构造函数的参数为什么是引用呢? string& operator=(const string & str); //赋值函数为什么返回值是引用呢?参数为什么是引用呢? ~string(); }; 下面我就给大家解释一下: class String1 { public: String1(const char*

引用参数,引用返回值

函数形参诗引用,程序输出如程序最后的注释,表明引用s代表的是对象s2. //函数中的引用 #include<iostream> using namespace std; class Sample { int x; public: Sample(int a): x(a) { cout<<"call constructor Sample(int a)"<<endl; } Sample(Sample &a): x(a.x) { cout <&

const参数,const返回值与const函数

在C++程序中,经常用const 来限制对一个对象的操作,例如,将一个变量定义为const 的: const  int  n=3; 则这个变量的值不能被修改,即不能对变量赋值. const 这个关键字经常出现在函数的定义中,而且会出现在不同的位置,比如: int  strcmp (const   char  *str1,const    char  *str2); const    int  & min (int  &, int  &); void  printMessage (c

c++ const: const 变量, const 输入参数, const 返回值, const 成员函数

看到const 关键字,C++程序员首先想到的可能是const 常量.这可不是良好的条件反射.如果只知道用const 定义常量,那么相当于把火药仅用于制作鞭炮.const 更大的魅力是它可以修饰函数的参数.返回值,甚至函数的定义体. const 是constant 的缩写,“恒定不变”的意思.被const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性.所以很多C++程序设计书籍建议:“Use const whenever you need”. 1.用const 修饰函数的参数