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作为外部变量的地址。

  • 相关阅读:
    【基础】jquery全选、反选、全不选代码
    【基础】jquery全选、反选、全不选代码
    收集一些程序员励志经典名言
    收集一些程序员励志经典名言
    收集一些程序员励志经典名言
    防止表单重复提交的解决方案整理
    Git使用教程
    2019牛客暑期多校训练营(第二场)J Subarray
    Hibernate-配置
    与项目欧拉速度比较:C vs Python与Erlang vs Haskell
  • 原文地址:https://www.cnblogs.com/cswuyg/p/1805026.html
Copyright © 2011-2022 走看看