• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            大龍的博客

            常用鏈接

            統計

            最新評論

            關于NRV優化

                在C++中,函數返回整數或指針是通過eax寄存器進行傳遞的,理解起來比較簡單。

                但是返回對象或結構體一直是令人感到困惑的問題。今天我整理了一下,將整個返回過程寫下來,以作備用。

             

                還是先通過一個例子來理解這個問題:

            首先,定義一個類Vector:

             

            class Vector
            {
            public:
                int x,y;
            };

             

            然后定義函數add()對Vector對象進行操作:

            Vector add(Vector& a, Vector & b)
            {
                Vector v;
                v.x = a.x + b.x;
                v.y = a.y + b.y;
                return v;
            }

             

            現在的問題是:

            如果調用如下語句:

            Vector a, b;
            Vector c = add(a, b);

            請問從a, b傳入函數開始,一共創建了多少個對象?

             

            在通常情況下我們會做出如下分析:

            1. 在add()函數中創建對象v。

            2. 函數返回,創建一個臨時變量__temp0,并將v的值拷貝到__temp0中。

            3. 最后創建對象c,通過操作符=,將__temp0中的對象拷貝到c中。



             

            但其實,我們會在后面看到,整個過程就只創建了1個對象:c。

             

            為了更清晰的分析整個調用過程,我們為Vector加上默認構造函數和拷貝構造函數,并增加一個靜態變量count用于統計構造函數調用次數:

            class Vector
            {
            public:
                static int count;

                static void init()
                {
                    count = 0;
                }

                int x,y;

                Vector()
                {
                    x = 0;
                    y = 0;
                    
                    // For analysis.
                    count ++;
                    printf("Default Constructor was called.[0x%08x]\n", this);
                }

                Vector(const Vector & ref)
                {
                    x = ref.x;
                    y = ref.y;

                    // For analysis.
                    count ++;
                    printf("Copy Constructor was called.[copy from 0x%08x to 0x%08x].\n", &ref, this);
                }

            };

            int Vector::count = 0;

             

            然后在main()函數中寫上調用代碼:

                Vector a, b;
                Vector::init();
                printf("\n-- Test add() --\n");
                Vector c = add(a, b);
                printf("---- Constructors were called %d times. ----\n\n\n", Vector::count);

             

            使用cl編譯。

            (注:Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86, Microsoft (R) Incremental Linker Version 10.00.40219.01)

            完成后,運行程序,得到如下結果:

            -- Test add() --
            Default Constructor was called.[0x0012fef8]
            Copy Constructor was called.[copy from 0x0012fef8 to 0x0012ff60].
            ---- Constructors were called 2 times. ----

             

            由此可知,在沒有優化的情況下,整個調用過程共創建了兩個對象:

            即:c和__temp0.

             

            整個調用過程偽代碼如下:

            首先add()函數被編譯器看做:

            void add(Vector& __result, Vector& a, Vector & b)
            {
                __result.x = a.x + b.x;
                __result.y = a.y + b.y;
                return;
            }

            而調用代碼同時被修改為:

                Vector a, b;
                Vector::init();
                printf("\n-- Test add() --\n");
                Vector __temp0;        // 構造函數.
                add(__temp0, a, b);
                Vector c(__temp0);    // 拷貝構造函數.
                printf("---- Constructors were called %d times. ----\n\n\n", Vector::count);

             



             

            現在就可以理解輸出結果了吧。

            這里要強調一點,看到”=”并不等于調用了Operator=()的代碼,以下三種情況其實是等效的,都只調用了拷貝構造函數:

            Vector b(a);
            Vector b = a;
            Vector b = Vector(a);

             

            最精彩的部分在于,如果你用

            cl /Ox

             

            編譯代碼,使優化達到最大,再次運行,得到如下結果:

            -- Test add() --
            Default Constructor was called.[0x0012ff74]
            ---- Constructors were called 1 times. ----

             

            這次,只調用了默認構造函數。這樣的修改被稱作Named Return Value(NRV) Optimization。

            什么是NRV優化呢,顧名思義,就是保存返回值的變量不再使用沒名沒姓的__temp0這樣的東西了,而是直接把c作為返回變量,因此應該將NRV翻譯為“有名字的返回變量”吧,侯捷翻譯的《深入探索C++對象模型》居然把它稱為“具名數值”,真是不知所云。

            言歸正傳,NVR優化的偽代碼如下:

                Vector c;
                add(c, a, b);

            NVR優化的最大好處就是不會再去調用那次多余拷貝構造函數了(把__temp0拷貝到c),因此《深入探索C++對象模型》67頁最下面才會說第一版沒有拷貝構造函數,所以不能進行優化。其實是指優化的意義不大,或者說沒有什么可優化的。



            但是這樣帶來的壞處是,如果你在拷貝構造函數里面放上與拷貝無關的代碼,比如我放入的printf和count++,那么這些東西就不會被調用了,產生優化前后代碼不一致問題。所以大家要在此注意一下。
            http://www.linuxso.com/linuxbiancheng/5726.html

            posted on 2012-03-28 17:30 大龍 閱讀(449) 評論(0)  編輯 收藏 引用

            国产亚洲精午夜久久久久久| 久久综合精品国产二区无码| 国产成人综合久久久久久| 7国产欧美日韩综合天堂中文久久久久 | 国产精品一久久香蕉国产线看 | 亚洲国产成人乱码精品女人久久久不卡 | 国产真实乱对白精彩久久| 亚洲欧美成人久久综合中文网| 无码人妻久久一区二区三区| 99热热久久这里只有精品68| 色天使久久综合网天天| 久久九九亚洲精品| 精品国产乱码久久久久软件| 国产激情久久久久影院小草| 久久无码人妻一区二区三区 | 一本色道久久综合狠狠躁篇| 99久久99这里只有免费费精品| 欧美激情精品久久久久久久| 精品久久久久久亚洲精品| 久久丝袜精品中文字幕| 青青草国产精品久久| 亚洲精品国产字幕久久不卡| 久久中文字幕视频、最近更新| 99久久无色码中文字幕| 亚洲国产一成人久久精品| 亚洲美日韩Av中文字幕无码久久久妻妇 | 久久精品欧美日韩精品| 手机看片久久高清国产日韩| 99久久精品免费看国产| 97超级碰碰碰碰久久久久| 国产亚洲婷婷香蕉久久精品| 久久国产精品77777| 亚洲AV无码久久精品成人| 亚洲午夜久久久久久噜噜噜| 久久精品亚洲AV久久久无码| 97精品依人久久久大香线蕉97 | 精品免费久久久久国产一区| 国产激情久久久久影院小草| 久久99精品国产麻豆不卡| 久久久精品久久久久特色影视| 国产激情久久久久影院|