• <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 閱讀(1383) 評論(4)  編輯 收藏 引用 所屬分類: C/C++

            構(gòu)造函數(shù)語義學(xué)

            --了解編譯器在構(gòu)造對象時背著我們干了什么勾當(dāng)

            Default Ctor 在需要的時候被構(gòu)建出來~

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

            例如:

                  class Foo(public:int val;);

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

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

            1.帶有Default Ctor 的member class object

            很好理解,既然內(nèi)部成員含有default ctor 那么我們創(chuàng)建新的對象時需要調(diào)用它,而我們并木有調(diào)用它的函數(shù),編譯器自然會幫我們提供一個。如果我們提供了default ctor ,那么編譯器會在我們提供的函數(shù)加入調(diào)用必須調(diào)用的member class 的default ctor。同樣的在每一個ctor里面都將在用戶寫的代碼之前調(diào)用需要調(diào)用的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 那么編譯器會創(chuàng)建一個,并在其中依照聲明順序依次調(diào)用dopey sneezy bashful的default ctor 然而如果:

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

            編譯器會擴(kuò)張為

            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 子類當(dāng)然要調(diào)用,編譯器會為想上面一樣為我們加上

            3.含有virtual functions的Class

            創(chuàng)建object 時候需要ctor 來設(shè)置好vptr

            4.帶有virtual base class

            virtual base 實現(xiàn)方法各有不同,然而共同點是必須是virtual base class 在其每一個derived class中的位置能夠與執(zhí)行期準(zhǔn)備妥當(dāng)

            X A B C 菱形繼承

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

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

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

            OK:

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

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

            錯的很easy了~

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

            再來看 Copy Ctor:

            copy ctor 負(fù)責(zé)用另一個對象初始化一個對象

            operator = 負(fù)責(zé)用另一個對象給一個對象賦值

            直接賦值時,傳參時,返回時可能調(diào)用Copy ctor

            Default member initialization~~~

            也就是memberwise 的initialization

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

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

            什么時候不可以呢~

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

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

            3  含有virtual func

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

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

            3 中:

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

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

            4中:

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

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

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

            程序轉(zhuǎn)化語義學(xué):

            X x0;

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

            轉(zhuǎn)化:重寫定義,初始化操作會被剝除   copy constructor 調(diào)用會被安插

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

             

            參數(shù)的初始化:

            一種策略導(dǎo)入暫時的object

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

            轉(zhuǎn)化:

            X _tmp;

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

            foo變成 void foo(X& x0);

            另一種是拷貝構(gòu)建:

            把實際參數(shù)直接建構(gòu)造應(yīng)該在的位置上,函數(shù)返回時局部對象的destructor 會執(zhí)行

            也就是說把x0建構(gòu)在foo 的函數(shù)執(zhí)行的地方

             

            返回值的初始化:

            X bar(){

                X xx;

                return xx;

            }

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

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

            2.return 指令之前安插一個copy constructor 調(diào)用操作 ,以便將傳回的object 內(nèi)容當(dāng)做上述參數(shù)的初值

            so 上面的程序變成了:

            void bar(X& _result){

              X xx;

              xx.X::X(); //編譯器產(chǎn)生的default ctor 調(diào)用

              _result.X::X(xx);//編譯器產(chǎn)生的copy ctor 調(diào)用

              return ;

            }

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

            而:

            bar.memfunc();//bar()傳回的X 調(diào)用成員函數(shù)

            變成:

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

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

            使用者的優(yōu)化:

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

                X xx;

                通過 y z 計算xx

               return xx;

            }

            這種情況下要iuxx被memberwise拷貝到編譯器產(chǎn)生的_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了

             

            編譯器的優(yōu)化:

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

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

            這個優(yōu)化的激活需要class提供一個copy ctor~~~~~~

             

            Copy ctor 要不要:

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

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

             

            成員們的初始化過程:

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

            1.初始化一個reference時 2.初始化一個const member 3.調(diào)用父類ctor而且有參數(shù)時4調(diào)用member class ctor 有參數(shù)

            其他情況呢:

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

            編譯器可能這么做:

            word::word{

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

                string tmp=string(0);

            _name.string::operator=(tmp);

            tmp.string::~string();

            cnt=0;

            }

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

            name.String::String(0);

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

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

            編譯器會一個個操作list中的式子,以適當(dāng)次序在ctor內(nèi)安插初始化操作在任何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相關(guān)的this指針已經(jīng)構(gòu)建妥當(dāng),只是要注意函數(shù)調(diào)用的member是否已經(jīng)構(gòu)建妥當(dāng)了即可

            ------         -  -  - ----------       --       --疲憊的結(jié)束線-          - -  -     - --            -           -----

            name return value TEST:

            ~~~~1 cl /od 不開優(yōu)化

            #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 創(chuàng)建
            I am in constructor         // rvo1創(chuàng)建
            I am in copy constructor // rvo1賦值給hiddern
            I am in destructor          // rvo1解構(gòu)
            I am in destructor          // hiddern解構(gòu)
            I am in destructor         //  rvo 解構(gòu)

            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創(chuàng)建
            I am in constructor  //hiddern 創(chuàng)建
            I am in destructor   //hiddern 解構(gòu)
            I am in destructor   //rvo解構(gòu)

            我不明白的是hiddern 怎么傳給rvo ,我猜可能是編譯器按照bitwise的復(fù)制方式進(jìn)行的,此時編譯器并沒有直接建構(gòu)結(jié)果于rvo上 ,看看下面的試驗:

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

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

            I am in constructor  //rvo創(chuàng)建
            I am in constructor  //hiddern 創(chuàng)建

            I am in operator =   //賦值操作~~
            I am in destructor   //hiddern 解構(gòu)
            I am in destructor   //rvo解構(gòu)

            ~~~~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);  //此時定義和賦值放到了一個表達(dá)式子
                return 0;
            }

            output:

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

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

            I am in constructor           // rvo構(gòu)建了 
            I am in destructor            // rvo析構(gòu)了

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

            ok~~~~~4個代碼完全一樣構(gòu)造析構(gòu)拷貝函數(shù)個數(shù)由2-6不等~~~over~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

             

            Feedback

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

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

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

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

            I am in constructor // rvo構(gòu)建了
            I am in copy constructor // rvo析構(gòu)了

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

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

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

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

            2010-03-11 16:32 by zmm
            @rikisand
            呵呵,因為沒有看到destroy,所以我運行了一下~
            久久亚洲AV成人无码| 国产精品欧美久久久久无广告 | 久久无码人妻一区二区三区| 久久大香香蕉国产| 日产精品久久久久久久| 熟妇人妻久久中文字幕| 亚洲国产小视频精品久久久三级| 欧美日韩中文字幕久久伊人| 99久久99久久精品国产片果冻| 久久精品国产精品亚洲人人| 九九精品99久久久香蕉| 久久精品国产免费观看三人同眠| 久久国产精品免费一区| 日本免费一区二区久久人人澡| 久久久久久亚洲精品影院| 国产精品久久久天天影视| 狠狠色丁香久久综合婷婷| 久久97精品久久久久久久不卡| 天天影视色香欲综合久久| 亚洲精品综合久久| 99久久国产免费福利| 日本久久久久久中文字幕| 亚洲愉拍99热成人精品热久久| 久久久这里有精品中文字幕| 亚洲欧美久久久久9999| 999久久久免费国产精品播放| 久久国产亚洲精品无码| 伊人久久大香线蕉亚洲| 一本久久综合亚洲鲁鲁五月天| 精品久久久久久国产免费了| 女人香蕉久久**毛片精品| 精品国产91久久久久久久| 无码人妻久久一区二区三区免费| 国产精品久久久久久久app | 国内精品综合久久久40p| 日本加勒比久久精品| 久久青青国产| 亚洲国产精品成人久久蜜臀| 要久久爱在线免费观看| 国产精品99久久久精品无码| 久久精品国产99久久久古代|