C++中的临时对象

来源:互联网 发布:淘宝人工客服在哪里找 编辑:程序博客网 时间:2024/05/10 07:03

临时对象的产生

C++中真正的临时对象是看不见的,它们不出现在你的源代码中,临时对象的产生在如下几个时刻:

1.  用构造函数作为隐式类型转换函数时,会创建临时对象。

class Integer{    public:       Integer(int i) :m_val(i) {}       ~Integer(){}     private:       int   m_val;}; void Calculate(Integer itgr){    // do something}

那么语句:  int  i = 10;

    Calculate(i);

会产生一个临时对象,作为实参传递到Calculate 函数中。


2.  建立一个没有命名的非堆(non-heap)对象,也就是无名对象时,会产生临时对象。

如:

        Integer& iref = Integer(5);     //错误

       切记:C++不允许用非const引用一个临时对象,要改为const引用,大概是因为临时对象不能改变,默认为const类型。

       const  Integer& iref = Integer(5);     //用无名临时对象初始化一个引用,等价于Integer iref(5);

        Integer  itgr = Integer(5);           //用一个无名临时对象拷贝构造另一个对象

       按理说,C++应先构造一个无名的临时对象,再用它来拷贝构造itgr,由于该临时对象拷贝构造 itgr 后,就失去了任何作用,所以对于这种类型(只起拷贝构造另一个对象的作用)的临时对象,C++特别将其看做: Integer itgr(5); 即直接以相同参数构造目标对象,省略了创建临时对象这一步。

  Calculate( Integer(5) );        //无名临时对象作为实参传递给形参,函数调

              //用表达式结束后,临时对象生命期结束,被析构.

3.  函数返回一个对象值时,会产生临时对象,函数中的返回值会以值拷贝的形式拷贝到被调函数栈中的一个临时对象。

 Integer Func() {      Integer itgr;      return itgr; }  void main() {      Integer in;      in = Func(); }
表达式 Func() 处创建了一个临时对象,用来存储Func() 函数中返回的对象,临时对象由 Func() 中返回的 itgr 对象拷贝构造(值传递),临时对象赋值给 in后,赋值表达式结束,临时对象被析构。见下图:

看看如下语句:

                      Integer& iRef = Func(); //改为 const 引用就行了

用一个临时对象去初始化iRef 引用,一旦该表达式执行结束,临时对象的生命周期结束,便被结束,iRef引用的尸体已经不存在,接下来任何对 iRef 的操作都是错误的。


很实用的测试例子

下面,来看看实际的测试结果,代码如下:

#include <iostream>#include <iomanip>using namespace std;class VECTOR3{public:    VECTOR3()       :x(0.0f),y(0.0f),z(0.0f)    {       std::cout<<"VECTOR3 Default Constructor "               <<std::setiosflags(std::ios_base::hex)<<this               <<std::endl;    }     VECTOR3(float fx, float fy, float fz)       :x(0.0f),y(0.0f),z(0.0f)    {       std::cout<<"VECTOR3 Parameter Constructor "               <<std::setiosflags(std::ios_base::hex)<<this               <<std::endl;    }     VECTOR3(const VECTOR3& rht)       :x(rht.x), y(rht.y), z(rht.z)    {       std::cout<<"VECTOR3 Copy Constructor "           <<std::setiosflags(std::ios_base::hex)<<this           <<" from rht : "           <<std::setiosflags(std::ios_base::hex)<<&rht           <<std::endl;    }     ~VECTOR3()    {       std::cout<<"VECTOR3 Destructor "               <<std::setiosflags(std::ios_base::hex)<<this               <<std::endl;    }     VECTOR3& operator = (const VECTOR3& rht)    {       if( &rht == this )           return *this;        x = rht.x;       y = rht.y;       z = rht.z;        std::cout<<"VECTOR3 operator = left oper : "               <<std::setiosflags(std::ios_base::hex)<<this               <<" right oper : "               <<std::setiosflags(std::ios_base::hex)<<&rht               <<std::endl;        return *this;    }private:    float x;    float y;    float z;friend VECTOR3 Func1();friend VECTOR3 Func2();}; VECTOR3 Func1(){    return VECTOR3(1.0f, 1.0f, 1.0f);} VECTOR3 Func2(){    VECTOR3 ret;    ret.x = 2.0f;    ret.y = 2.0f;    ret.z = 2.0f;    return ret;}  int main(){VECTOR3 v1 = Func1();    v1 = Func1(); VECTOR3 v2 = Func2();        VECTOR3 v3;v3 = Func2();return 0;}

分析:

<1>.VECTOR3 v1 = Func1();

该语句的执行过程本该是:

    1>. 在 Func1() 中构造一个无名对象

    2>. 由 Func1() 中的无名对象拷贝构造调用表达式处的临时对象

    3>. 再由临时对象拷贝构造v1

    4>. Func1() 返回,析构无名对象

    5>. 整个语句结束,析构临时对象

但是c++ 会优化上述过程,省略了 1>. 2>. 处的临时对象创建,直接以1.0f, 1.0f, 1.0f 为参数构造v1,这样只会有一次构造函数的调用。结果

如图:

<2>.v1 = Func1();

该语句的执行过程本该是:

    1>. 在 Func1() 中构造一个无名对象

    2>. 由 Func1() 中的无名对象拷贝构造调用表达式处的临时对象

    3>. 再由临时对象赋值给v1 (赋值运算符)

    4>. Func1() 返回,析构无名对象

    5>. 整个语句结束,析构临时对象

但是c++ 会优化上述过程,省略了 2>. 处的无名临时对象创建,直接以1.0f, 1.0f, 1.0f 为参数构造调用表达式处的临时对象,因为是赋值,所以这个临时对象是无法被优化的,赋值完毕后,表达式结束,临时对象被析构。结果如图:



<3>.VECTOR3 v2 = Func2();

       这个和<1>一样,优化为以直接以临时变量初始化v2

结果如图:


<4>.VECTOR3 v3v3 = Func2();

执行过程如下:

    1>. 构造v3

    2>. 进入Func2(),构造ret

    3>. 返回ret,用ret拷贝构造到调用表达式处的临时对象

    4>. Func2()结束,ret被析构

    5>. 临时对象赋值给v3

    6>. 赋值表达式结束,析构临时对象

结果如图:


程序最后析构 v1、v2、v3


综上所述,可得如下结论:

<1>. 在使用一个临时对象( 可能是无名对象 或者 返回对象值时 ) 创建构造另一个对象的过程的中,c++会优化掉该临时对象的产生,直接以相同参数调用相关构造函数构或者直接调用拷贝构造函数到目标对象.

<2>. 若不是对象创建,而是对象赋值,则在赋值表达式的右值处的临时对象创建不能省略,临时对象赋值给左值后,表达式结束,临时对象被析构。


转自:http://www.cnblogs.com/daocaoren/archive/2011/07/19/2110258.html (对自己不十分认同的地方做了修改)

原创粉丝点击