• <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>

            深入探索C++對象模型讀書筆記 (二)

            Posted on 2010-03-03 22:23 rikisand 閱讀(1378) 評論(4)  編輯 收藏 引用 所屬分類: C/C++

            構造函數語義學

            --了解編譯器在構造對象時背著我們干了什么勾當

            Default Ctor 在需要的時候被構建出來~

            什么需要? 是編譯器需要而不是程序的需要,所以不要期望編譯器生成的Ctor會幫我們把我們的成員變量初始化為零。那是程序的需要,也就是我們程序員的任務····

            例如:

                  class Foo(public:int val;);

                  void foo_bar(){Foo bar; if(bar.val)dosth;} 不要期望編譯器幫我們初始化它為0。只有global變量會初始化為0,初始化val這樣的變量需要我們自己寫代碼的~~

            Default Ctor分為trival(沒用的)和non-trival的,下面討論什么時候編譯器需要ctor 也就是有用的ctor,這時候如果我們沒有提供一個默認的default ctor 它會幫我們合成一個的~~

            1.帶有Default Ctor 的member class object

            很好理解,既然內部成員含有default ctor 那么我們創建新的對象時需要調用它,而我們并木有調用它的函數,編譯器自然會幫我們提供一個。如果我們提供了default ctor ,那么編譯器會在我們提供的函數加入調用必須調用的member class 的default ctor。同樣的在每一個ctor里面都將在用戶寫的代碼之前調用需要調用的default ctor -------看例子:

            class Dopey();class Sneezy{public:Sneezy(int val);Sneezy();};class Bashful{public:BashFul();};

            class Snow_white{public: Dopey dopey;Sneezy sneezy;Bashful bashful;private:int muble};

            如果Snow_white沒有定義default ctor 那么編譯器會創建一個,并在其中依照聲明順序依次調用dopey sneezy bashful的default ctor 然而如果:

            Snow_white::Snow_white():sneezy(1024){muble=2045;}

            編譯器會擴張為

            Snow_white::Snow_white():sneezy(1024){

              dopey.Dopey::Dopey();

              sneezy.Sneezy::Sneezy(1024);

              bashful.Bashful::Bashful();

              /////////   explicit user code

              muble = 2048;

            }

            2.派生自帶有Default ctor 的 base class

            同樣道理如果父類有Default ctor 子類當然要調用,編譯器會為想上面一樣為我們加上

            3.含有virtual functions的Class

            創建object 時候需要ctor 來設置好vptr

            4.帶有virtual base class

            virtual base 實現方法各有不同,然而共同點是必須是virtual base class 在其每一個derived class中的位置能夠與執行期準備妥當

            X A B C 菱形繼承

            void foo(const A*pa){pa->i=1024;}

            foo(new A);foo(new C);

            知道pa 后 i在對象中的位置并不是固定的,而是在運行時真正確定pa指向什么對象A還是C才能確定的,因此需要設定一個指向基類subobject的指針,所以需要ctor工作了

            OK:

            1.任何class 如果沒有定義Default ctor 就會被合成一個

            2.合成出來的Default ctor 會明確設定每一個data member值

            錯的很easy了~

            -------------------------------------------------------------------------------

            再來看 Copy Ctor:

            copy ctor 負責用另一個對象初始化一個對象

            operator = 負責用另一個對象給一個對象賦值

            直接賦值時,傳參時,返回時可能調用Copy ctor

            Default member initialization~~~

            也就是memberwise 的initialization

            他會把每一個data member (內建的或者派生的)從一個object 拷貝到另一個object中去

            如果object允許bitwise的拷貝那么編譯器就不用生成一個nontrival的default copy ctor

            什么時候不可以呢~

            1 內含一個member object 而后者含有copy constructor (聲明或者合成的)

            2  繼承一個base class 后者有copy ctor

            3  含有virtual func

            4  派生自一個繼承鏈,其中有virtual base class

            1和2 中編譯器會把member 或者baseclass copy ctor 調用安插在合成的copy ctor 中

            3 中:

            如果兩個同樣類型的object賦值時,沒有問題因為他們的vptr相同

            但是考慮子類賦值給父類,此時vptr需要更改,那么此時不具有bitwise特性,因此需要編譯器來加入語句正確更新vptr

            4中:

            每個編譯器都承諾必須讓derived class 中的virtual base class object 在執行期間準備妥當,維護位置完整性是編譯器的責任。bitwise copy 有可能會破壞這個位置所以編譯器需要在自己合成的copy ctor 中作出仲裁

            同樣問題發生在繼承體系中子類向父類賦值時,由于對象模型問題,直接bitwise復制可能會導致base class object 的破壞(后面章節會有討論)

            --------------------------------------------------------------------------------

            程序轉化語義學:

            X x0;

            void foo(){X x1(x0); X x2=x0; X x3=X(x0);}

            轉化:重寫定義,初始化操作會被剝除   copy constructor 調用會被安插

            void foo(){ X x1;X x2; X x3;  x1.X::X(x0); x2.X::X(x0); x3.X::X(x0);}

             

            參數的初始化:

            一種策略導入暫時的object

            void foo(X x0);X xx; foo(xx);

            轉化:

            X _tmp;

            _tmp.X::X(x0); foo(_tmp);

            foo變成 void foo(X& x0);

            另一種是拷貝構建:

            把實際參數直接建構造應該在的位置上,函數返回時局部對象的destructor 會執行

            也就是說把x0建構在foo 的函數執行的地方

             

            返回值的初始化:

            X bar(){

                X xx;

                return xx;

            }

            返回值如何從局部對象xx拷貝而來呢?

            一種方法:1.加上額外參數,類型是class object的reference,這個參數用來放置被拷貝建構的返回值 (注意拷貝建構也就是說他被放在應該在的位置,也就是說不是局部變量了)

            2.return 指令之前安插一個copy constructor 調用操作 ,以便將傳回的object 內容當做上述參數的初值

            so 上面的程序變成了:

            void bar(X& _result){

              X xx;

              xx.X::X(); //編譯器產生的default ctor 調用

              _result.X::X(xx);//編譯器產生的copy ctor 調用

              return ;

            }

            現在編譯器必須轉換bar的調用操作 X xx=bar();轉換成 X xx; bar(xx); // 注意不用copy ctor了直接操作在xx上了 如果編譯器做了優化 這就是named return value 優化

            而:

            bar.memfunc();//bar()傳回的X 調用成員函數

            變成:

            X _tmp;  (bar(_tmp),_tmp).memfunc();

            同樣道理函數指針 X(*pf)(); 變成 void (*pf)(X&);

            使用者的優化:

            X bar(const T& y,const T& z){

                X xx;

                通過 y z 計算xx

               return xx;

            }

            這種情況下要iuxx被memberwise拷貝到編譯器產生的_result中,

            如果定義 ctor來利用yz計算xx則:

            X bar(const T& y,const T& z){

                 return X(y,z);

            }

            變成:

            bar(X& result){

                 result . X::X(y,z);

                 return;

            }

            無需copy ctor了

             

            編譯器的優化:

            如上所述bar中返回了具名數值 named value,因此編譯器有可能自己優化,方法是以result取代named return value

            bar(X& result){_result.X::X(); 直接處理result 并不處理變量xx然后復制給result 這樣就優化了}

            這個優化的激活需要class提供一個copy ctor~~~~~~

             

            Copy ctor 要不要:

            如果一個class 符合bitwise的要求那么此時member wise 的拷貝已經高效簡潔 無需加入了

            但是如果class需要大量的member wise 初始化操作,如用傳值方式返回objects,如果是這樣提供一個copy ctor 可以激活nRV named return value 優化,這就很合理了

             

            成員們的初始化過程:

            什么時候必須用初始化列表:

            1.初始化一個reference時 2.初始化一個const member 3.調用父類ctor而且有參數時4調用member class ctor 有參數

            其他情況呢:

            class word{string name;int cnt;public: name=0;cnt=0;}

            編譯器可能這么做:

            word::word{

                name.String::string();調用string的default ctor

                string tmp=string(0);

            _name.string::operator=(tmp);

            tmp.string::~string();

            cnt=0;

            }

            顯然name放到初始化列表會更有效率 ,會變成

            name.String::String(0);

            而cnt這種內建類型則沒有關系,放不放到初始化列表沒有效率上的差別

            初始化列表究竟讓編譯器做了什么????

            編譯器會一個個操作list中的式子,以適當次序在ctor內安插初始化操作在任何explicit user code 之前。

            注意的地方:

                   list中的次序是按照members聲明次序決定而不是list 中的排列順序決定。

            例如:class x{int i;int j; X(int val):j(val),i(j)}

            錯了 i先聲明則i首先賦予val 然后用未初始化的j賦給i。。。

            可以這樣X::X(int val):j(val){i=j;}

            由于會安插在explicit code 之前 所以沒問題 會變成 j=val;   i=j;

            可否用member functions 初始化成員??

            答案是可以的,因為和objects相關的this指針已經構建妥當,只是要注意函數調用的member是否已經構建妥當了即可

            ------         -  -  - ----------       --       --疲憊的結束線-          - -  -     - --            -           -----

            name return value TEST:

            ~~~~1 cl /od 不開優化

            #include <iostream>
            using namespace std;
            class RVO
            {
            public:

                RVO(){printf("I am in constructor\n");}
                RVO (const RVO& c_RVO) {printf ("I am in copy constructor\n");}
                ~RVO(){printf ("I am in destructor\n");}
                int mem_var;      
            };
            RVO MyMethod (int i)
            {
                RVO rvo1;
                rvo1.mem_var = i;
                return (rvo1);
            }
            int main()
            {
                RVO rvo;rvo=MyMethod(5);
            }

            輸出:

            I am in constructor         //rvo 創建
            I am in constructor         // rvo1創建
            I am in copy constructor // rvo1賦值給hiddern
            I am in destructor          // rvo1解構
            I am in destructor          // hiddern解構
            I am in destructor         //  rvo 解構

            A MyMethod (A &_hiddenArg, B &var)
            {
               A retVal;
               retVal.A::A(); // constructor for retVal
               retVal.member = var.value + bar(var);
               _hiddenArg.A::A(retVal);  // the copy constructor for A
               return;
            retVal.A::~A();  // destructor for retVal
            
            }
            A MyMethod(A &_hiddenArg, B &var)
            {
               _hiddenArg.A::A();
               _hiddenArg.member = var.value + bar(var);
               Return
            }
            ~~~~2 cl /o2 代碼同上
            output

            I am in constructor  //rvo創建
            I am in constructor  //hiddern 創建
            I am in destructor   //hiddern 解構
            I am in destructor   //rvo解構

            我不明白的是hiddern 怎么傳給rvo ,我猜可能是編譯器按照bitwise的復制方式進行的,此時編譯器并沒有直接建構結果于rvo上 ,看看下面的試驗:

            注:明白了, 結果直接建構在hiddern,然后通過operator = 傳給rvo 。沒有copy ctor因為拷貝構造函數是負責初始化的,而operator = 才是用來賦值的.

            經過代碼證明是對的,如果重載賦值運算符 輸出變成:

            I am in constructor  //rvo創建
            I am in constructor  //hiddern 創建

            I am in operator =   //賦值操作~~
            I am in destructor   //hiddern 解構
            I am in destructor   //rvo解構

            ~~~~3 cl /od

            #include <iostream>
            using namespace std;
            class RVO
            {
            public:

                RVO(){printf("I am in constructor\n");}
                RVO (const RVO& c_RVO) {printf ("I am in copy constructor\n");}
                ~RVO(){printf ("I am in destructor\n");}
                int mem_var;      
            };
            RVO MyMethod (int i)
            {
                RVO rvo1;
                rvo1.mem_var = i;
                return (rvo1);
            }
            void abc(RVO& i){
            }
            int main()
            {
                RVO rvo=MyMethod(5);  //此時定義和賦值放到了一個表達式子
                return 0;
            }

            output:

            I am in constructor           // rvo1 創建 注意 跟上面的第一種情況下的hiddern一樣 rvo并沒有調用ctor
            I am in copy constructor   // rvo1 拷貝構造給rvo 此時沒有hiddern了 直接構建rvo了
            I am in destructor            // rvo1 析構
            I am in destructor            // rvo1 解構

            ~~~~3 cl /o2  再來~~~~ NRV出馬

            I am in constructor           // rvo構建了 
            I am in destructor            // rvo析構了

            此時 mymethod中的一切都直接反映在rvo身上

            ok~~~~~4個代碼完全一樣構造析構拷貝函數個數由2-6不等~~~over~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

            Feedback

            # re: 深入探索C++對象模型讀書筆記 (二)  回復  更多評論   

            2010-03-11 09:13 by zmm
            不錯,很基礎的

            # re: 深入探索C++對象模型讀書筆記 (二)  回復  更多評論   

            2010-03-11 13:56 by zmm
            ~~~~3 cl /o2 再來~~~~ NRV出馬

            I am in constructor // rvo構建了
            I am in copy constructor // rvo析構了

            這里打錯了吧,是in destructor吧~

            # re: 深入探索C++對象模型讀書筆記 (二)  回復  更多評論   

            2010-03-11 14:01 by rikisand
            恩 ~~ 改過來啦 你看的好仔細啊~~

            # re: 深入探索C++對象模型讀書筆記 (二)  回復  更多評論   

            2010-03-11 16:32 by zmm
            @rikisand
            呵呵,因為沒有看到destroy,所以我運行了一下~
            性做久久久久久免费观看| 国产精品99久久久久久董美香| 久久精品国产久精国产一老狼| 伊人久久综合无码成人网 | 国产一区二区精品久久岳| 亚洲精品国产第一综合99久久| 欧美va久久久噜噜噜久久| 国产成人精品久久一区二区三区av| 久久久久免费精品国产| 51久久夜色精品国产| 久久天天躁狠狠躁夜夜96流白浆 | 欧美日韩中文字幕久久伊人| 久久久久久毛片免费看| 久久国产乱子精品免费女| 精品久久久久成人码免费动漫| 99久久伊人精品综合观看| 色狠狠久久AV五月综合| 亚洲日本va午夜中文字幕久久| 99久久精品免费国产大片| 国产精品久久久久久久久免费| 国内精品久久久久影院老司| 99久久99久久精品国产片| 国产Av激情久久无码天堂| 伊人久久大香线蕉av一区| 亚洲а∨天堂久久精品| 国产成人久久精品麻豆一区| 狠狠色噜噜狠狠狠狠狠色综合久久| 亚洲精品乱码久久久久久中文字幕 | 久久久久久亚洲精品影院| 久久久久国产视频电影| 久久国产成人精品国产成人亚洲| 九九99精品久久久久久| 久久香蕉国产线看观看乱码| 国产成人久久AV免费| MM131亚洲国产美女久久| 亚洲国产精品无码久久| 久久久无码精品亚洲日韩蜜臀浪潮| 伊人久久大香线蕉AV色婷婷色| 无码人妻精品一区二区三区久久久| 精品多毛少妇人妻AV免费久久| 一本色综合网久久|