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

            chaosuper85

            C++博客 首頁 新隨筆 聯系 聚合 管理
              118 Posts :: 0 Stories :: 3 Comments :: 0 Trackbacks

                多繼承與虛函數重復

                既然說到了多繼承,那么還有一個問題可能會需要解決,那就是如果兩個父類里都有相同的虛函數定義,在子對象的布局里會是怎么樣個情況?是否依然可以將這個虛函數指向到正確的實現代碼上呢?

                修改前面一個源代碼,在parent2的接口里增加下面的虛函數定義:

                virtual int fun1(){cout<<"parent2::fun1()"<<endl;return 0;};

                上面的fun1的定義與parent1類里的完全重復相同(類型,參數列表),增加上面的代碼后立即開始編譯,程序正常編譯通過。運行之,得到下面的結果:

                child1::fun1()

                child1::fun2()

                這個程序居然正確的完成了執行,編譯器在其中做了些怎樣的工作,是怎么樣避免掉隊fun1函數的沖突問題呢?

                讓我們來看看這個時候的child1的對象布局:
             class child1    size(8):
                    +---
                    | +--- (base class parent1)
             0      | | {vfptr}
                    | +---
                    | +--- (base class parent2)
             4      | | {vfptr}
                    | +---
                    +---

            child1::$vftable@parent1@:
                    | &child1_meta
                    |  0
             0      | &child1::fun1

            child1::$vftable@parent2@:
                    | -4
             0      | &child1::fun2
             1      | &thunk: this-=4; goto child1::fun1

            child1::fun1 this adjustor: 0
            child1::fun2 this adjustor: 4

             


                恩~~~還是兩個vfptr在child1的對象布局里(不一樣就怪啦,呵呵),但是第二個vfptr所指的虛函數表的內容有所變化哦!

                注意看紅色字體部分,虛函數表里并沒有直接填寫child::fun1的代碼,而是多了一個 &thunk: this-=4;然后才goto child1::fun1!注意到一個關鍵名詞thunk了吧?沒錯,vc在這里使用了名為thunk的技術,避免了虛函數fun1在兩個基類里重復出現導致的沖突問題!(除了thunk,還有其他方法可以解決此類問題的)。

                現在,我們知道為什么相同的虛函數不會在子類里出現沖突的情況了。

                但是,倘若我們在基類里就是由兩個沖突的普通函數,而不是虛函數,是個怎樣的情況呢?

                多繼承產生的沖突與虛繼承,虛基類

                我們在parent1和parent2里添加一個相同的函數void fun3(),然后再進行編譯,通過了!查看類對象布局,跟上面的完全一致。但是在main函數里調用chobj.fun3()的時候,編譯器卻不再能正確編譯了,并且會提示“error C2385: 對”fun3“的訪問不明確”的錯誤信息,沒錯,編譯器不知道你要訪問哪個fun3了。

                如何解決這樣的多繼承帶來的問題呢,其實有一個簡單的做法。就是在方法前限定引用的具體是哪個類的函數,比如:chobj.parent1::fun3();  ,這樣的寫法就寫明了是要調用chobj的父類parent1里的fun3()函數!

                我們再看看另外一種情況,從parent1和parent2里抹去剛才添加的fun3函數,將之放到一個共同的基類里:
                 class commonbase

                {

                public:

                void fun3(){cout<<"commonbase::fun3()"<<endl;}

                };
             


                而parent1和parent2都修改為從此類繼承。可以看到,在這個情況下,依然需要使用chobj.parent1::fun3();  的方式才可以正確調用到fun3,難道,在這種情況下,就不能自然的使用chobj.fun3()這樣的方式了嗎?

                虛繼承可以解決這個問題——我們在parent1和parent2繼承common類的地方添加上一個關鍵詞virtual,如下:
                 class parent1:virtual public commonbase

                {

                public:

                virtual int fun1(){cout<<"parent1::fun1()"<<endl;return 0;};

                };
             


                給parent2也同樣的處理,然后再次編譯,這次chobj.fun3()可以編譯通過了!!!

                編譯器這次又在私下里做了哪些工作了呢????
             class child1    size(16):
                    +---
                    | +--- (base class parent1)
             0      | | {vfptr}
             4      | | {vbptr}
                    | +---
                    | +--- (base class parent2)
             8      | | {vfptr}
            12      | | {vbptr}
                    | +---
                    +---
                    +--- (virtual base commonbase)
                    +---

            child1::$vftable@parent1@:
                    | &child1_meta
                    |  0
             0      | &child1::fun1

            child1::$vftable@parent2@:
                    | -8
             0      | &child1::fun2
             1      | &thunk: this-=8; goto child1::fun1

            child1::$vbtable@parent1@:
             0      | -4
             1      | 12 (child1d(parent1+4)commonbase)

            child1::$vbtable@parent2@:
             0      | -4
             1      | 4 (child1d(parent2+4)commonbase)

            child1::fun1 this adjustor: 0
            child1::fun2 this adjustor: 8

            vbi:       class  offset o.vbptr  o.vbte fVtorDisp
                  commonbase      16       4       4 0

             


                這次變化可大了去了!!!

                首先,可以看到兩個類parent1和parent2的對象布局里,都多了一個vbptr的指針。而在child1的對象布局里,還有一個virtual base commonbase的虛擬基類。再看看兩個vbptr的內容:

                12 (child1d(parent1+4)commonbase) 這個很好理解,從parent1的vbptr開始,偏移12個字節,指向的是virtual base commonbase!

                再看看4 (child1d(parent2+4)commonbase) ,從parent2的vbptr開始,便宜4個字節,也指向了virtual base commonbase!

                這下明白了。虛基類在child1里只有一個共同的對象布局了,所以就可以直接用chobj.fun3()啦,當然,在commonbase里的其他成員變量此時也可以同樣的方式訪問了!

                雖然解決方案有了,但是在一個系統的設計里,如果有一個基類出現多繼承沖突的情況,大部分情況下都說明這樣的設計是有問題的,應該盡量避免這樣的設計,并且盡量用純虛函數,來提取一些抽象的接口類,把共同的方法接口都抽取出來,通常就能避免多繼承的問題。

             

            posted on 2009-08-05 17:46 chaosuper 閱讀(146) 評論(0)  編輯 收藏 引用
            色综合久久久久综合99| 久久91精品久久91综合| 久久这里有精品| 伊人久久精品无码二区麻豆| 亚洲中文精品久久久久久不卡| 亚洲av伊人久久综合密臀性色| 久久99中文字幕久久| 性高朝久久久久久久久久| 亚洲AV无码成人网站久久精品大| av国内精品久久久久影院| 久久婷婷五月综合色99啪ak| 国产精品久久久久影院嫩草| 色婷婷综合久久久久中文字幕| 久久九九精品99国产精品| 婷婷久久综合九色综合九七| 嫩草影院久久国产精品| 久久精品国产亚洲AV香蕉| 亚洲国产二区三区久久| 亚洲中文久久精品无码ww16| 日韩精品无码久久一区二区三| 1000部精品久久久久久久久| 99精品国产99久久久久久97 | 国産精品久久久久久久| 色偷偷久久一区二区三区| 亚洲国产成人精品女人久久久| 欧美精品一区二区精品久久| 久久久久亚洲av无码专区喷水| 久久狠狠爱亚洲综合影院| 欧美粉嫩小泬久久久久久久| 久久精品无码一区二区日韩AV| 色成年激情久久综合| 亚洲成色999久久网站| 久久99精品国产一区二区三区| 久久久久人妻精品一区二区三区 | 久久国产精品久久精品国产| 色欲综合久久中文字幕网| 亚洲中文精品久久久久久不卡| 亚洲国产一成人久久精品| 东方aⅴ免费观看久久av| 日韩人妻无码精品久久免费一| 国产精品久久新婚兰兰|