青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

posts - 28, comments - 179, trackbacks - 0, articles - 1
  C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

虛繼承與虛基類的本質(zhì)

Posted on 2007-06-12 12:21 chemz 閱讀(36278) 評(píng)論(15)  編輯 收藏 引用 所屬分類: C++
                              虛繼承與虛基類的本質(zhì)
    虛繼承和虛基類的定義是非常的簡(jiǎn)單的,同時(shí)也是非常容易判斷一個(gè)繼承是否是虛繼承
的,雖然這兩個(gè)概念的定義是非常的簡(jiǎn)單明確的,但是在C++語言中虛繼承作為一個(gè)比較生
僻的但是又是絕對(duì)必要的組成部份而存在著,并且其行為和模型均表現(xiàn)出和一般的繼承體系
之間的巨大的差異(包括訪問性能上的差異),現(xiàn)在我們就來徹底的從語言、模型、性能和
應(yīng)用等多個(gè)方面對(duì)虛繼承和虛基類進(jìn)行研究。
    首先還是先給出虛繼承和虛基類的定義。
    虛繼承:在繼承定義中包含了virtual關(guān)鍵字的繼承關(guān)系;
    虛基類:在虛繼承體系中的通過virtual繼承而來的基類,需要注意的是:
            struct CSubClass : public virtual CBase {}; 其中CBase稱之為CSubClass
            的虛基類,而不是說CBase就是個(gè)虛基類,因?yàn)镃Base還可以不不是虛繼承體系
            中的基類。
    有了上面的定義后,就可以開始虛繼承和虛基類的本質(zhì)研究了,下面按照語法、語義、
模型、性能和應(yīng)用五個(gè)方面進(jìn)行全面的描述。

    1. 語法
       語法有語言的本身的定義所決定,總體上來說非常的簡(jiǎn)單,如下:
           struct CSubClass : public virtual CBaseClass {};
       其中可以采用public、protected、private三種不同的繼承關(guān)鍵字進(jìn)行修飾,只要
       確保包含virtual就可以了,這樣一來就形成了虛繼承體系,同時(shí)CBaseClass就成為
       了CSubClass的虛基類了。
       其實(shí)并沒有那么的簡(jiǎn)單,如果出現(xiàn)虛繼承體系的進(jìn)一步繼承會(huì)出現(xiàn)什么樣的狀況呢?
       如下所示:
            /*
             * 帶有數(shù)據(jù)成員的基類
             */
            struct CBaseClass1
            {
                CBaseClass1( size_t i ) : m_val( i ) {}
            
                size_t m_val;
            };
            /*
             * 虛擬繼承體系
             */
            struct CSubClassV1 : public virtual CBaseClass1
            {
                CSubClassV1( size_t i ) : CBaseClass1( i ) {}
            };           
            struct CSubClassV2 : public virtual CBaseClass1
            {
                CSubClassV2( size_t i ) : CBaseClass1( i ) {}
            };           
            struct CDiamondClass1 : public CSubClassV1, public CSubClassV2
            {
                CDiamondClass1( size_t i ) : CBaseClass1( i ), CSubClassV1( i ), CSubClassV2( i ) {}
            };           
            struct CDiamondSubClass1 : public CDiamondClass1
            {
                CDiamondSubClass1( size_t i ) : CBaseClass1( i ), CDiamondClass1( i ) {}
            };
       注意上面代碼中的CDiamondClass1和CDiamondSubClass1兩個(gè)類的構(gòu)造函數(shù)初始化列
       表中的內(nèi)容。可以發(fā)現(xiàn)其中均包含了虛基類CBaseClass1的初始化工作,如果沒有這
       個(gè)初始化語句就會(huì)導(dǎo)致編譯時(shí)錯(cuò)誤,為什么會(huì)這樣呢?一般情況下不是只要在
       CSubClassV1和CSubClassV2中包含初始化就可以了么?要解釋該問題必須要明白虛
       繼承的語義特征,所以參看下面語義部分的解釋。
      
    2. 語義
       從語義上來講什么是虛繼承和虛基類呢?上面僅僅是從如何在C++語言中書寫合法的
       虛繼承類定義而已。首先來了解一下virtual這個(gè)關(guān)鍵字在C++中的公共含義,在C++
       語言中僅僅有兩個(gè)地方可以使用virtual這個(gè)關(guān)鍵字,一個(gè)就是類成員虛函數(shù)和這里
       所討論的虛繼承。不要看這兩種應(yīng)用場(chǎng)合好像沒什么關(guān)系,其實(shí)他們?cè)诒尘罢Z義上
       具有virtual這個(gè)詞所代表的共同的含義,所以才會(huì)在這兩種場(chǎng)合使用相同的關(guān)鍵字。
       那么virtual這個(gè)詞的含義是什么呢?
       virtual在《美國(guó)傳統(tǒng)詞典[雙解]》中是這樣定義的:
           adj.(形容詞)
           1. Existing or resulting in essence or effect though not in actual
              fact, form, or name:
              實(shí)質(zhì)上的,實(shí)際上的:雖然沒有實(shí)際的事實(shí)、形式或名義,但在實(shí)際上或效
              果上存在或產(chǎn)生的;
           2. Existing in the mind, especially as a product of the imagination.
              Used in literary criticism of text.
              虛的,內(nèi)心的:在頭腦中存在的,尤指意想的產(chǎn)物。用于文學(xué)批評(píng)中。
       我們采用第一個(gè)定義,也就是說被virtual所修飾的事物或現(xiàn)象在本質(zhì)上是存在的,
       但是沒有直觀的形式表現(xiàn),無法直接描述或定義,需要通過其他的間接方式或手段
       才能夠體現(xiàn)出其實(shí)際上的效果。
       那么在C++中就是采用了這個(gè)詞意,不可以在語言模型中直接調(diào)用或體現(xiàn)的,但是確
       實(shí)是存在可以被間接的方式進(jìn)行調(diào)用或體現(xiàn)的。比如:虛函數(shù)必須要通過一種間接的
       運(yùn)行時(shí)(而不是編譯時(shí))機(jī)制才能夠激活(調(diào)用)的函數(shù),而虛繼承也是必須在運(yùn)行
       時(shí)才能夠進(jìn)行定位訪問的一種體制。存在,但間接。其中關(guān)鍵就在于存在、間接和共
       享這三種特征。
       對(duì)于虛函數(shù)而言,這三個(gè)特征是很好理解的,間接性表明了他必須在運(yùn)行時(shí)根據(jù)實(shí)際
       的對(duì)象來完成函數(shù)尋址,共享性表象在基類會(huì)共享被子類重載后的虛函數(shù),其實(shí)指向
       相同的函數(shù)入口。
       對(duì)于虛繼承而言,這三個(gè)特征如何理解呢?存在即表示虛繼承體系和虛基類確實(shí)存在,
       間接性表明了在訪問虛基類的成員時(shí)同樣也必須通過某種間接機(jī)制來完成(下面模型
       中會(huì)講到),共享性表象在虛基類會(huì)在虛繼承體系中被共享,而不會(huì)出現(xiàn)多份拷貝。
       那現(xiàn)在可以解釋語法小節(jié)中留下來的那個(gè)問題了,“為什么一旦出現(xiàn)了虛基類,就必
       須在沒有一個(gè)繼承類中都必須包含虛基類的初始化語句”。由上面的分析可以知道,
       虛基類是被共享的,也就是在繼承體系中無論被繼承多少次,對(duì)象內(nèi)存模型中均只會(huì)
       出現(xiàn)一個(gè)虛基類的子對(duì)象(這和多繼承是完全不同的),這樣一來既然是共享的那么
       每一個(gè)子類都不會(huì)獨(dú)占,但是總還是必須要有一個(gè)類來完成基類的初始化過程(因?yàn)?br>       所有的對(duì)象都必須被初始化,哪怕是默認(rèn)的),同時(shí)還不能夠重復(fù)進(jìn)行初始化,那到
       底誰應(yīng)該負(fù)責(zé)完成初始化呢?C++標(biāo)準(zhǔn)中(也是很自然的)選擇在每一次繼承子類中
       都必須書寫初始化語句(因?yàn)槊恳淮卫^承子類可能都會(huì)用來定義對(duì)象),而在最下層
       繼承子類中實(shí)際執(zhí)行初始化過程。所以上面在每一個(gè)繼承類中都要書寫初始化語句,
       但是在創(chuàng)建對(duì)象時(shí),而僅僅會(huì)在創(chuàng)建對(duì)象用的類構(gòu)造函數(shù)中實(shí)際的執(zhí)行初始化語句,
       其他的初始化語句都會(huì)被壓制不調(diào)用。
      
    3. 模型
       為了實(shí)現(xiàn)上面所說的三種語義含義,在考慮對(duì)象的實(shí)現(xiàn)模型(也就是內(nèi)存模型)時(shí)就
       很自然了。在C++中對(duì)象實(shí)際上就是一個(gè)連續(xù)的地址空間的語義代表,我們來分析虛
       繼承下的內(nèi)存模型。
       3.1. 存在
           也就是說在對(duì)象內(nèi)存中必須要包含虛基類的完整子對(duì)象,以便能夠完成通過地址
           完成對(duì)象的標(biāo)識(shí)。那么至于虛基類的子對(duì)象會(huì)存放在對(duì)象的那個(gè)位置(頭、中間、
           尾部)則由各個(gè)編譯器選擇,沒有差別。(在VC8中無論虛基類被聲明在什么位置,
           虛基類的子對(duì)象都會(huì)被放置在對(duì)象內(nèi)存的尾部)
       3.2. 間接
           間接性表明了在直接虛基承子類中一定包含了某種指針(偏移或表格)來完成通
           過子類訪問虛基類子對(duì)象(或成員)的間接手段(因?yàn)樘摶愖訉?duì)象是共享的,
           沒有確定關(guān)系),至于采用何種手段由編譯器選擇。(在VC8中在子類中放置了
           一個(gè)虛基類指針vbc,該指針指向虛函數(shù)表中的一個(gè)slot,該slot中存放則虛基
           類子對(duì)象的偏移量的負(fù)值,實(shí)際上就是個(gè)以補(bǔ)碼表示的int類型的值,在計(jì)算虛
           基類子對(duì)象首地址時(shí),需要將該偏移量取絕對(duì)值相加,這個(gè)主要是為了和虛表
           中只能存放虛函數(shù)地址這一要求相區(qū)別,因?yàn)榈刂肥窃a表示的無符號(hào)int類型
           的值)
       3.3. 共享
           共享表明了在對(duì)象的內(nèi)存空間中僅僅能夠包含一份虛基類的子對(duì)象,并且通過
           某種間接的機(jī)制來完成共享的引用關(guān)系。在介紹完整個(gè)內(nèi)容后會(huì)附上測(cè)試代碼,
           體現(xiàn)這些內(nèi)容。
    4. 性能
       由于有了間接性和共享性兩個(gè)特征,所以決定了虛繼承體系下的對(duì)象在訪問時(shí)必然
       會(huì)在時(shí)間和空間上與一般情況有較大不同。
       4.1. 時(shí)間
           在通過繼承類對(duì)象訪問虛基類對(duì)象中的成員(包括數(shù)據(jù)成員和函數(shù)成員)時(shí),都
           必須通過某種間接引用來完成,這樣會(huì)增加引用尋址時(shí)間(就和虛函數(shù)一樣),
           其實(shí)就是調(diào)整this指針以指向虛基類對(duì)象,只不過這個(gè)調(diào)整是運(yùn)行時(shí)間接完成的。
           (在VC8中通過打開匯編輸出,可以查看*.cod文件中的內(nèi)容,在訪問虛基類對(duì)象
           成員時(shí)會(huì)形成三條mov間接尋址語句,而在訪問一般繼承類對(duì)象時(shí)僅僅只有一條mov
           常量直接尋址語句)
       4.2. 空間
           由于共享所以不同在對(duì)象內(nèi)存中保存多份虛基類子對(duì)象的拷貝,這樣較之多繼承
           節(jié)省空間。
    5. 應(yīng)用
       談了那么多語言特性和內(nèi)容,那么在什么情況下需要使用虛繼承,而一般應(yīng)該如何使
       用呢?
       這個(gè)問題其實(shí)很難有答案,一般情況下如果你確性出現(xiàn)多繼承沒有必要,必須要共享
       基類子對(duì)象的時(shí)候可以考慮采用虛繼承關(guān)系(C++標(biāo)準(zhǔn)ios體系就是這樣的)。由于每
       一個(gè)繼承類都必須包含初始化語句而又僅僅只在最底層子類中調(diào)用,這樣可能就會(huì)使
       得某些上層子類得到的虛基類子對(duì)象的狀態(tài)不是自己所期望的(因?yàn)樽约旱某跏蓟Z
       句被壓制了),所以一般建議不要在虛基類中包含任何數(shù)據(jù)成員(不要有狀態(tài)),只
       可以作為接口類來提供。

附錄:測(cè)試代碼
#include <ctime>
#include <iostream>

/*
 * 帶有數(shù)據(jù)成員的基類
 */
struct CBaseClass1
{
    CBaseClass1( size_t i ) : m_val( i ) {}

    size_t m_val;
};
/*
 * 虛擬繼承體系
 */
struct CSubClassV1 : public virtual CBaseClass1
{
    CSubClassV1( size_t i ) : CBaseClass1( i ) {}
};

struct CSubClassV2 : public virtual CBaseClass1
{
    CSubClassV2( size_t i ) : CBaseClass1( i ) {}
};

struct CDiamondClass1 : public CSubClassV1, public CSubClassV2
{
    CDiamondClass1( size_t i ) : CBaseClass1( i ), CSubClassV1( i ), CSubClassV2( i ) {}
};

struct CDiamondSubClass1 : public CDiamondClass1
{
    CDiamondSubClass1( size_t i ) : CBaseClass1( i ), CDiamondClass1( i ) {}
};
/*
 * 正常繼承體系
 */
struct CSubClassN1 : public CBaseClass1
{
    CSubClassN1( size_t i ) : CBaseClass1( i ) {}
};
struct CSubClassN2 : public CBaseClass1
{
    CSubClassN2( size_t i ) : CBaseClass1( i ) {}
};
struct CMultiClass1 : public CSubClassN1, public CSubClassN2
{
    CMultiClass1( size_t i ) : CSubClassN1( i ), CSubClassN2( i ) {}
};
struct CMultiSubClass1 : public CMultiClass1
{
    CMultiSubClass1( size_t i ) : CMultiClass1( i ) {}
};
/*
 * 不帶有數(shù)據(jù)成員的接口基類
 */
struct CBaseClass2
{
    virtual void func() {};
    virtual ~CBaseClass2() {}
};
/*
 * 虛擬繼承體系
 */
// struct CBaseClassX { CBaseClassX() {i1 = i2 = 0xFFFFFFFF;} size_t i1, i2;};
struct CSubClassV3 : public virtual CBaseClass2
{
};
struct CSubClassV4 : public virtual CBaseClass2
{
};
struct CDiamondClass2 : public CSubClassV3, public CSubClassV4
{
};
struct CDiamondSubClass2 : public CDiamondClass2
{
};

/*
 * 正常繼承體系
 */
struct CSubClassN3 : public CBaseClass2
{
};
struct CSubClassN4 : public CBaseClass2
{
};
struct CMultiClass2 : public CSubClassN3, public CSubClassN4
{
};
struct CMultiSubClass2 : public CMultiClass2
{
};

/*
 * 內(nèi)存布局用類聲明.
 */
struct CLayoutBase1
{
    CLayoutBase1() : m_val1( 0 ), m_val2( 1 ) {}

    size_t m_val1, m_val2;
};
struct CLayoutBase2
{
    CLayoutBase2() : m_val1( 3 ) {}

    size_t m_val1;
};
struct CLayoutSubClass1 : public virtual CBaseClass1, public CLayoutBase1, public CLayoutBase2
{
    CLayoutSubClass1() : CBaseClass1( 2 ) {}
};


#define MAX_TEST_COUNT 1000 * 1000 * 16
#define TIME_ELAPSE() ( std::clock() - start * 1.0 ) / CLOCKS_PER_SEC

int main( int argc, char *argv[] )
{
    /*
     * 類體系中的尺寸.
     */
    std::cout << "================================ sizeof ================================" << std::endl;
    std::cout << "    ----------------------------------------------------------------" << std::endl;
    std::cout << "sizeof( CBaseClass1 )       = " << sizeof( CBaseClass1 ) << std::endl;
    std::cout << std::endl;
    std::cout << "sizeof( CSubClassV1 )       = " << sizeof( CSubClassV1 ) << std::endl;
    std::cout << "sizeof( CSubClassV2 )       = " << sizeof( CSubClassV2 ) << std::endl;
    std::cout << "sizeof( CDiamondClass1 )    = " << sizeof( CDiamondClass1 ) << std::endl;
    std::cout << "sizeof( CDiamondSubClass1 ) = " << sizeof( CDiamondSubClass1 ) << std::endl;
    std::cout << std::endl;
    std::cout << "sizeof( CSubClassN1 )       = " << sizeof( CSubClassN1 ) << std::endl;
    std::cout << "sizeof( CSubClassN2 )       = " << sizeof( CSubClassN2 ) << std::endl;
    std::cout << "sizeof( CMultiClass1 )      = " << sizeof( CMultiClass1 ) << std::endl;
    std::cout << "sizeof( CMultiSubClass1 )   = " << sizeof( CMultiSubClass1 ) << std::endl;

    std::cout << "    ----------------------------------------------------------------" << std::endl;
    std::cout << "sizeof( CBaseClass2 )       = " << sizeof( CBaseClass2 ) << std::endl;
    std::cout << std::endl;
    std::cout << "sizeof( CSubClassV3 )       = " << sizeof( CSubClassV3 ) << std::endl;
    std::cout << "sizeof( CSubClassV4 )       = " << sizeof( CSubClassV4 ) << std::endl;
    std::cout << "sizeof( CDiamondClass2 )    = " << sizeof( CDiamondClass2 ) << std::endl;
    std::cout << "sizeof( CDiamondSubClass2 ) = " << sizeof( CDiamondSubClass2 ) << std::endl;
    std::cout << std::endl;
    std::cout << "sizeof( CSubClassN3 )       = " << sizeof( CSubClassN3 ) << std::endl;
    std::cout << "sizeof( CSubClassN4 )       = " << sizeof( CSubClassN4 ) << std::endl;
    std::cout << "sizeof( CMultiClass2 )      = " << sizeof( CMultiClass2 ) << std::endl;
    std::cout << "sizeof( CMultiSubClass2 )   = " << sizeof( CMultiSubClass2 ) << std::endl;
    /*
     * 對(duì)象內(nèi)存布局
     */
    std::cout << "================================ layout ================================" << std::endl;
    std::cout << "    --------------------------------MI------------------------------" << std::endl;
    CLayoutSubClass1 *lsc = new CLayoutSubClass1;
    std::cout << "sizeof( CLayoutSubClass1 )   = " << sizeof( CLayoutSubClass1 ) << std::endl;
    std::cout << "CLayoutBase1 offset of CLayoutSubClass1 is " << (char*)(CLayoutBase1 *)lsc - (char*)lsc << std::endl;
    std::cout << "CBaseClass1  offset of CLayoutSubClass1 is " << (char*)(CBaseClass1  *)lsc - (char*)lsc << std::endl;
    std::cout << "CLayoutBase2 offset of CLayoutSubClass1 is " << (char*)(CLayoutBase2 *)lsc - (char*)lsc << std::endl;

    int *ptr = (int*)lsc;
    std::cout << "vbc in CLayoutSubClass1 is " << *(int*)ptr[3] << std::endl;

    delete lsc;

    std::cout << "    --------------------------------SI------------------------------" << std::endl;
    CSubClassV1 *scv1 = new CSubClassV1( 1 );
    std::cout << "sizeof( CSubClassV1 )   = " << sizeof( CSubClassV1 ) << std::endl;
    std::cout << "CBaseClass1 offset of CSubClassV1 is " << (char*)(CBaseClass1 *)scv1 - (char*)scv1 << std::endl;

    ptr = (int*)scv1;
    std::cout << "vbc in CSubClassV1 is " << *(int*)ptr[0] << std::endl;

    delete scv1;

    /*
     * 性能測(cè)試
     */
    std::cout << "================================ Performance ================================" << std::endl;
    double times[4];
    size_t idx = 0;

    CSubClassV1 *ptr1 = new CDiamondClass1( 1 );
    std::clock_t start = std::clock();
    {
        for ( size_t i = 0; i < MAX_TEST_COUNT; ++i )
            ptr1->m_val = i;
    }
    times[idx++] = TIME_ELAPSE();
    delete static_cast<CDiamondClass1*>( ptr1 );

    CSubClassN1 *ptr2 = new CMultiClass1( 0 );
    start = std::clock();
    {
        for ( size_t i = 0; i < MAX_TEST_COUNT; ++i )
            ptr2->m_val = i;
    }
    times[idx++] = TIME_ELAPSE();
    delete static_cast<CMultiClass1*>( ptr2 );

    std::cout << "CSubClassV1::ptr1->m_val " << times[0] << " s" << std::endl;
    std::cout << "CSubClassN1::ptr2->m_val " << times[1] << " s" << std::endl;

    return 0;
}

測(cè)試環(huán)境:
    軟件環(huán)境:Visual Studio2005 Pro + SP1, boost1.34.0
    硬件環(huán)境:PentiumD 3.0GHz, 4G RAM
測(cè)試數(shù)據(jù):
================================ sizeof ================================
    ----------------------------------------------------------------
sizeof( CBaseClass1 )       = 4

sizeof( CSubClassV1 )       = 8
sizeof( CSubClassV2 )       = 8
sizeof( CDiamondClass1 )    = 12
sizeof( CDiamondSubClass1 ) = 12

sizeof( CSubClassN1 )       = 4
sizeof( CSubClassN2 )       = 4
sizeof( CMultiClass1 )      = 8
sizeof( CMultiSubClass1 )   = 8
    ----------------------------------------------------------------
sizeof( CBaseClass2 )       = 4

sizeof( CSubClassV3 )       = 8
sizeof( CSubClassV4 )       = 8
sizeof( CDiamondClass2 )    = 12
sizeof( CDiamondSubClass2 ) = 12

sizeof( CSubClassN3 )       = 4
sizeof( CSubClassN4 )       = 4
sizeof( CMultiClass2 )      = 8
sizeof( CMultiSubClass2 )   = 8
================================ layout ================================
    --------------------------------MI------------------------------
sizeof( CLayoutSubClass1 )   = 20
CLayoutBase1 offset of CLayoutSubClass1 is 0
CBaseClass1  offset of CLayoutSubClass1 is 16
CLayoutBase2 offset of CLayoutSubClass1 is 8
vbc in CLayoutSubClass1 is -12
    --------------------------------SI------------------------------
sizeof( CSubClassV1 )   = 8
CBaseClass1 offset of CSubClassV1 is 4
vbc in CSubClassV1 is 0
================================ Performance ================================
CSubClassV1::ptr1->m_val 0.062 s
CSubClassN1::ptr2->m_val 0.016 s

結(jié)果分析:
    1. 由于虛繼承引入的間接性指針?biāo)詫?dǎo)致了虛繼承類的尺寸會(huì)增加4個(gè)字節(jié);
    2. 由Layout輸出可以看出,虛基類子對(duì)象被放在了對(duì)象的尾部(偏移為16),并且vbc
       指針必須緊緊的接在虛基類子對(duì)象的前面,所以vbc指針?biāo)赶虻膬?nèi)容為“偏移 - 4”;
    3. 由于VC8將偏移放在了虛函數(shù)表中,所以為了區(qū)分函數(shù)地址和偏移,所以偏移是用補(bǔ)
       碼int表示的負(fù)值;
    4. 間接性可以通過性能來看出,在虛繼承體系同通過指針訪問成員時(shí)的時(shí)間一般是一般
       類訪問情況下的4倍左右,符合匯編語言輸出文件中的匯編語句的安排。
       

Feedback

# re: 虛繼承與虛基類的本質(zhì)  回復(fù)  更多評(píng)論   

2007-06-12 12:25 by chemz
http://m.shnenglu.com/chemz/archive/2007/05/31/25189.html文章中jazz提到的那段代碼為何編譯出錯(cuò)的原因在這篇文章中可以找到。
根據(jù)文章中的語義小節(jié),虛基類的子對(duì)象必須要在每一個(gè)子類中都包含初始化語句所以,僅僅有Usable是虛基類的友元是不行的,還必須將D作為虛基類的友元。如下:
class Usable;
class D;

class Usable_lock {
friend class Usable;
friend class D;
private:
Usable_lock() {}
Usable_lock(const Usable_lock&) {}
};

class Usable : public virtual Usable_lock {
// ...
public:
Usable();
Usable(char*);
// ...
};

Usable a;

class DD : public Usable { };

DD dd;

# re: 虛繼承與虛基類的本質(zhì)  回復(fù)  更多評(píng)論   

2007-06-13 12:16 by 黃大仙
非常棒

# re: 虛繼承與虛基類的本質(zhì)  回復(fù)  更多評(píng)論   

2008-10-16 18:16 by frank.sunny
語法語義上的解釋很連貫,我能看懂
但是模型和性能方面,可能我功力還不夠吧

總得來說,讓我收獲不小

# re: 虛繼承與虛基類的本質(zhì)  回復(fù)  更多評(píng)論   

2009-02-24 17:29 by 哈子豬
求vbc in CLayoutSubClass1 is和vbc in CSubClassV1時(shí) 代碼:
int *ptr = (int*)lsc;
std::cout << "vbc in CLayoutSubClass1 is " << *(int*)ptr[3] << std::endl;


ptr = (int*)scv1;
std::cout << "vbc in CSubClassV1 is " << *(int*)ptr[0] << std::endl;
沒能不懂,特別是為啥一個(gè)是1 一個(gè)是3 ,請(qǐng)大師指點(diǎn)

# re: 虛繼承與虛基類的本質(zhì)  回復(fù)  更多評(píng)論   

2009-04-30 11:42 by ee
連看了兩篇文章,很佩服

言語之間能看出來絕對(duì)是高手中的高手。

不會(huì)一下太對(duì)不起你了!

# re: 虛繼承與虛基類的本質(zhì)  回復(fù)  更多評(píng)論   

2009-05-04 10:24 by hunan people
看完了,謝謝這種自己寫的東西,有條有理,樓主辛苦了。受益非淺。

# re: 虛繼承與虛基類的本質(zhì)  回復(fù)  更多評(píng)論   

2011-04-28 15:22 by 楊旭東
寫得很棒

# re: 虛繼承與虛基類的本質(zhì)  回復(fù)  更多評(píng)論   

2011-09-08 22:50 by ww
good!

# re: 虛繼承與虛基類的本質(zhì)  回復(fù)  更多評(píng)論   

2012-07-03 11:17 by BreakMind
寫的太糟糕了,讓人看不懂。

不是我笨,我只能說,如果你能夠把文章寫得深入淺出,那么你才是真的高手,你嘛,偽高手。

# re: 虛繼承與虛基類的本質(zhì)  回復(fù)  更多評(píng)論   

2012-07-03 14:02 by BreakMind
這篇文章,真的是很爛,大家不要再看了,我自己看了兩天,仍然看不懂。開始以為是自己不認(rèn)真看,但是后來還是覺得云里霧里,這里我看到另外的兩篇文章,都比這篇強(qiáng)很多,大家可以看一下

http://www.cnblogs.com/itech/archive/2009/03/01/1399996.html

http://archive.cnblogs.com/a/2043794/

# re: 虛繼承與虛基類的本質(zhì)[未登錄]  回復(fù)  更多評(píng)論   

2012-07-04 11:27 by 無名
反正我是看懂了,的確看這篇文章需要的基礎(chǔ)..

# re: 虛繼承與虛基類的本質(zhì)  回復(fù)  更多評(píng)論   

2012-09-24 17:01 by 81300687
1. 由于虛繼承引入的間接性指針?biāo)詫?dǎo)致了虛繼承類的尺寸會(huì)增加4個(gè)字節(jié);
2. 由Layout輸出可以看出,虛基類子對(duì)象被放在了對(duì)象的尾部(偏移為16),并且vbc
指針必須緊緊的接在虛基類子對(duì)象的前面,所以vbc指針?biāo)赶虻膬?nèi)容為“偏移 - 4”;
************************************************************
文章沒有考慮字節(jié)對(duì)齊的問題, 而敲死了尺寸就是增加4個(gè)字節(jié), 可以試試把成員變量改成double類型試試,然后可以不同的pack(n)指令, 看看布局是如何變化的~~
一兩句話講不清楚~~~ 但是要精確到字節(jié)的話,就必須考慮字節(jié)對(duì)齊的原因。不能隨意敲死它就是增加幾個(gè)字節(jié)。

# re: 虛繼承與虛基類的本質(zhì)[未登錄]  回復(fù)  更多評(píng)論   

2012-10-12 15:53 by Benson
virtual 的語義我認(rèn)為是“允許重疊”,因此是“虛”的(就像在實(shí)物上蓋了一塊有實(shí)物影像的玻璃,最終看來只有一個(gè)實(shí)物),從而避免二義性的問題。例如基類成員函數(shù) f(),如果不加virtual修飾,子類如果有相同名字的成員函數(shù) f()就會(huì)產(chǎn)生二義性,所以編譯是不能通過的。同樣,對(duì)與基類,在多重繼承時(shí)可能會(huì)出現(xiàn)兩個(gè)基類,因此需要加上virtual來使它們“重疊”在一起,避免二義性。

# re: 虛繼承與虛基類的本質(zhì)  回復(fù)  更多評(píng)論   

2013-06-07 09:27 by 小菜
我運(yùn)行了下,檢查了,MI下delete lsc 上一行有錯(cuò)誤,不應(yīng)該加上*,就是
*(int*)ptr[3]去掉*。

# re: 虛繼承與虛基類的本質(zhì)  回復(fù)  更多評(píng)論   

2014-05-13 08:27 by foolbread
你好,這個(gè)類CSubClassV1的大小為8,是因?yàn)楸旧砜疹愓?字節(jié),和加一個(gè)vbptr為4,內(nèi)存對(duì)齊的緣故變成8的嗎?
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            亚洲午夜成aⅴ人片| 影音先锋欧美精品| 欧美性事免费在线观看| 亚洲成人在线视频播放| 性色av一区二区三区| 亚洲国产综合91精品麻豆| 亚洲男人av电影| 欧美日韩伦理在线| 一本色道久久综合一区 | 一区二区三区 在线观看视频| 欧美1区视频| 亚洲国产第一页| 欧美h视频在线| 蜜臀a∨国产成人精品| 在线精品在线| 男人的天堂亚洲| 欧美成黄导航| 夜夜爽99久久国产综合精品女不卡 | 国产日韩欧美精品在线| 欧美一区二区私人影院日本| 亚洲一区二区免费看| 国产欧美精品日韩区二区麻豆天美| 午夜精品福利在线观看| 午夜精品福利视频| 国产亚洲欧美另类中文| 久久综合久久美利坚合众国| 久久久一二三| 日韩视频在线你懂得| 日韩一级在线| 国产午夜精品一区二区三区欧美| 久久久久99| 欧美成人午夜| 性久久久久久久久久久久| 亚洲欧美日本日韩| 狠狠综合久久av一区二区老牛| 美女黄毛**国产精品啪啪| 欧美成人国产| 亚洲欧美精品在线| 久久精品国产久精国产一老狼| 亚洲国产三级网| 一区二区不卡在线视频 午夜欧美不卡'| 国产精品羞羞答答| 久热精品视频在线| 欧美色偷偷大香| 老司机免费视频一区二区三区| 免费一区视频| 性欧美xxxx视频在线观看| 久久久国产精品亚洲一区| 亚洲九九精品| 性8sex亚洲区入口| aa级大片欧美三级| 久久精品99国产精品日本| 99re66热这里只有精品4| 欧美亚洲免费电影| 99精品视频免费观看视频| 欧美影院视频| 中文有码久久| 免费视频最近日韩| 久久久久久久999精品视频| 欧美激情第9页| 麻豆国产va免费精品高清在线| 亚洲欧洲视频在线| 国模大胆一区二区三区| 亚洲美女黄色片| 一区免费观看| 欧美一区二区免费观在线| 亚洲一区二区三区视频播放| 免费精品视频| 米奇777在线欧美播放| 国产精品极品美女粉嫩高清在线| 欧美xx69| 亚洲高清激情| 久久成人精品| 欧美在线视频观看免费网站| 欧美日本一区二区视频在线观看| 免费亚洲电影在线| 国产一区二区三区四区在线观看| 一区二区精品| 亚洲视频在线一区| 欧美精品一区二区视频| 欧美激情一区三区| 在线看欧美日韩| 久久久精品一区二区三区| 久久精品人人做人人综合| 国产乱码精品一区二区三区五月婷 | 国模吧视频一区| 校园激情久久| 久久免费高清| 好吊视频一区二区三区四区| 亚欧成人在线| 久久综合中文字幕| 1000精品久久久久久久久 | 亚洲人www| 老司机一区二区三区| 欧美福利在线观看| 亚洲日韩第九十九页| 欧美国产日韩一区二区在线观看| 亚洲电影毛片| 在线一区二区视频| 欧美午夜精品久久久久久浪潮| 99热在线精品观看| 欧美一区二区免费观在线| 国产免费成人av| 欧美一区国产一区| 免费不卡亚洲欧美| 亚洲人体1000| 欧美日韩精品免费观看视一区二区| 亚洲黄色免费| 亚洲深夜影院| 国产亚洲欧美日韩精品| 久久精品国产欧美激情| 欧美成人亚洲| 亚洲视频一区二区| 国产日韩欧美在线视频观看| 久久香蕉国产线看观看网| 亚洲欧洲综合另类在线| 午夜精品一区二区三区在线| 国产亚洲精品福利| 免费看黄裸体一级大秀欧美| 99在线精品观看| 久久久久久久久久看片| 亚洲人成在线影院| 国产精品网站视频| 久久亚洲精选| 一区二区欧美在线观看| 久久久亚洲午夜电影| 欧美午夜精品久久久久久孕妇 | 亚洲精品免费观看| 欧美一区二区三区婷婷月色| 激情小说另类小说亚洲欧美| 欧美激情精品久久久六区热门| 一区二区三区回区在观看免费视频| 久久精品国产欧美激情| 亚洲精品视频免费在线观看| 国产精品国产| 老司机免费视频一区二区| 正在播放欧美一区| 午夜在线精品| 午夜在线视频观看日韩17c| 欧美视频在线观看 亚洲欧| 亚洲理论在线观看| 亚洲国产你懂的| 男女激情久久| 亚洲精品一二| 亚洲精品国产精品乱码不99按摩| 欧美—级a级欧美特级ar全黄| 在线看一区二区| 亚洲国产精品va| 一本久道久久综合狠狠爱| 午夜精品福利一区二区三区av| 欧美第十八页| 久久精品最新地址| 欧美激情a∨在线视频播放| 亚洲一区二区在线视频| 这里只有精品丝袜| 狠狠v欧美v日韩v亚洲ⅴ| 欧美一区二区三区免费视频| 一区二区三区四区五区精品| 国产在线观看91精品一区| 亚洲麻豆av| 亚洲二区在线观看| 羞羞视频在线观看欧美| 亚洲精品婷婷| 亚洲韩国青草视频| 国产在线成人| 国产日产欧产精品推荐色| 欧美日韩aaaaa| 蜜臀久久99精品久久久画质超高清 | 一区二区免费在线播放| 亚洲国产日韩欧美在线99| 久久午夜国产精品| 久久精品动漫| 午夜精品久久久久久久久| 一区二区三区高清| 99国产精品99久久久久久| 91久久精品国产91久久性色| 好看的日韩av电影| 国产中文一区二区| 亚洲作爱视频| 亚洲精选91| 亚洲人www| 最新国产乱人伦偷精品免费网站| 免费看精品久久片| 美日韩精品免费| 欧美电影免费观看大全| 另类天堂av| 欧美激情中文字幕在线| 欧美激情按摩| 91久久在线| 亚洲毛片av| 一本色道久久88精品综合| 中文国产成人精品| 亚洲女人av| 欧美一区国产一区| 久久久av毛片精品| 欧美成人亚洲| 国产精品va在线播放| 国产精品久久久久久久久久久久久久| 国产精品午夜在线| 亚洲电影免费在线观看|