zoukankan      html  css  js  c++  java
  • 自己动手理解NRV优化

    自己动手理解NRV优化

    2010.6.29

    烛秋

    2010.8.20整理

    说明:本文整理自:http://blog.csdn.net/wuxupeng999/archive/2010/06/29/5701513.aspx

    一、NRV的简单理解

      NRV是Named Return Value的简称。NRV优化简单的说:有一条语句,A a = f();其中f()是一个函数,函数里边申请了一个A的对象b,然后把对象b返回。在对象返回的时候,一般情况下要调用拷贝函数,把函数f()里边的局部对象b拷贝到函数外部的对象a。但是如果用了NRV优化,那就不必要调用拷贝构造函数,编译器可以这样做,把a的地址传递进函数f(),然后不让f()申请要返回的对象b的空间,用a的地址来代替b的地址,这样当要返回对象b的时候,就不必要拷贝了,因为b就是a,省去了局部变量b,省去了拷贝的过程。

    二、动手测试

        书上得来终觉浅。动手测试才是王道。

    测试代码如下:

    /////////////////////////////////////////////////
    #include <iostream>
    #include <cstring>
    using namespace std;
    class A
    {
    public:
    	A()
    	{
    		cout<<"construct"<<endl;
    		strcpy(name,"zhangsan");
    	}
    	~A()
    	{
    		cout<<"destruct"<<endl;
    	}
    	A(const A& temp)
    	{
    		cout<<"copy"<<endl;
    		strcpy(name,temp.name);
    	}
    private:
    	char name[16];
    };
    ////////////////////////////
    A f()
    {
    	cout<<"------------function f----------"<<endl;
    	A a;
    	return a;
    }
    ////////////////////////////
    int	main()
    {
    	cout<<"------------define-------------"<<endl;
    	A b = f();//A b(f());
    	return 0;
    }
    /*
    GCC编译:
    运行结果:
    ------------define-------------
    ------------function f----------
    construct
    destruct
    */
    ////////////////////////////
    ////////////////////////////
    /*
    VS2005
    debug下输出:
    ------------define-------------
    ------------function f----------
    construct
    copy
    destruct
    destruct
    */
    ////////////////////////////
    /*VS2005
    release下输出:
    ------------define-------------
    ------------function f----------
    construct
    destruct
    */
    /*G++以及vs2005的release,这就是NRV之后的结果*/
    
    

     

    测试环境:32位机器,vs2005编译器。

    测试部分截图

    图 1 debug模式下main()函数

     

    图 2 debug模式下f()函数

     

    图 3 release模式下 main()函数

     

    图 4 release模式下f()函数

    三、分析

         (1)debug模式

      分析图 1和图 2可以发现在调用f()函数时,传进了一个参数,这个参数就是拷贝构造函数的目标对象。而拷贝构造函数的源对象就是在f()函数里边定义的局部对象。构造对象的过程是这样的:首先在main()函数中申请对象b的空间,注意此时没有构造该对象,仅有地址而已;然后进入f()函数,构造f()函数中的对象a,接着调用拷贝构造函数构造main()函数中的对象b,地址是在main()函数时申请的;结束。

         (2)release模式

      分析图 3和图 4可以发现在调用f()函数时,通过esi传递了外部对象b的地址,在f()函数里边没有申请局部对象的空间,没调用构造函数(内联了),只是输出"construct",也没调用strcpy函数,而是通过寄存器把"zhangsan"拷贝到esi所指向的地址。release模式做了很大的优化!构造对象的过程是这样的:首先在main()函数申请对象b的空间,注意此时仅有地址而已;然后进入f()函数,构造对象b;结束。

          (3)总结

      可以这样理解:debug模式下,给函数传递外部变量b的地址,函数内先申请一个局部变量a,接着对a操作,最后调用拷贝构造函数把a拷贝到外部变量b,结束返回。release模式下,给函数传递外部变量b的地址,函数内不申请局部变量a的空间,把b作为a的空间,接着对a操作,结束返回。

      release模式下,局部变量构造函数的操作还是有的,就是没有申请空间,通过寄存器拷贝字符串"zhangsan"的过程就是在调用构造函数,不过看起来被内联在f()函数里边了。可以在f()函数返回之前,局部变量定义之后增加几行代码,再进行测试。

    四、关于测试

        1、 如何测试?为了方便测试可以输出一些字符,然后在OD里使用超级字符串查找插件,方便定位。

        2、如何知道有没有申请空间?看函数入口处有没有类似于sub esp,XX的指令,在debug模式下会发现f()函数里边有申请局部变量,而release模式下f()函数没有申请局部变量。

        3、一般情况下,堆栈的局部变量会采用[ebp-XX]的方式访问,而通过堆栈传递的参数会采用[ebp+XX]的方式访问。另外要注意在函数采用寄存器传递参数,例如:this指针采用ecx寄存器,esi作为外部变量的地址。

  • 相关阅读:
    LSMW TIPS
    Schedule agreement and Delfor
    Running VL10 in the background 13 Oct
    analyse idoc by creation date
    New Journey Prepare
    EDI error
    CBSN NEWS
    Listen and Write 18th Feb 2019
    Microsoft iSCSI Software Target 快照管理
    通过 Microsoft iSCSI Software Target 提供存储服务
  • 原文地址:https://www.cnblogs.com/cswuyg/p/1805026.html
Copyright © 2011-2022 走看看