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

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

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

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

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

附錄:測試代碼
#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;
    /*
     * 對象內(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;

    /*
     * 性能測試
     */
    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;
}

測試環(huán)境:
    軟件環(huán)境:Visual Studio2005 Pro + SP1, boost1.34.0
    硬件環(huán)境:PentiumD 3.0GHz, 4G RAM
測試數(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. 由于虛繼承引入的間接性指針所以導致了虛繼承類的尺寸會增加4個字節(jié);
    2. 由Layout輸出可以看出,虛基類子對象被放在了對象的尾部(偏移為16),并且vbc
       指針必須緊緊的接在虛基類子對象的前面,所以vbc指針所指向的內(nèi)容為“偏移 - 4”;
    3. 由于VC8將偏移放在了虛函數(shù)表中,所以為了區(qū)分函數(shù)地址和偏移,所以偏移是用補
       碼int表示的負值;
    4. 間接性可以通過性能來看出,在虛繼承體系同通過指針訪問成員時的時間一般是一般
       類訪問情況下的4倍左右,符合匯編語言輸出文件中的匯編語句的安排。
       

Feedback

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

2007-06-12 12:25 by chemz
http://m.shnenglu.com/chemz/archive/2007/05/31/25189.html文章中jazz提到的那段代碼為何編譯出錯的原因在這篇文章中可以找到。
根據(jù)文章中的語義小節(jié),虛基類的子對象必須要在每一個子類中都包含初始化語句所以,僅僅有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ì)  回復  更多評論   

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

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

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

總得來說,讓我收獲不小

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

2009-02-24 17:29 by 哈子豬
求vbc in CLayoutSubClass1 is和vbc in CSubClassV1時 代碼:
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;
沒能不懂,特別是為啥一個是1 一個是3 ,請大師指點

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

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

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

不會一下太對不起你了!

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2014-05-13 08:27 by foolbread
你好,這個類CSubClassV1的大小為8,是因為本身空類占1字節(jié),和加一個vbptr為4,內(nèi)存對齊的緣故變成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>
            在线观看三级视频欧美| 欧美福利专区| 国产精品入口| 久热精品视频在线观看一区| 亚洲午夜在线| 一区二区三区视频观看| 亚洲第一中文字幕| 久久久久久伊人| 久久国产精品久久久久久电车| 亚洲影视在线| 亚洲欧美日韩精品久久久久| 国产精品99久久久久久宅男| 国产精品夜夜夜| 亚洲欧洲一区二区三区久久| 久久se精品一区精品二区| 久久国产精品毛片| 亚洲视频一区在线| 亚洲欧美日韩国产一区| 久久久国产一区二区三区| 免播放器亚洲| 日韩视频在线观看| 久久国产欧美| 欧美视频不卡| 在线观看91精品国产入口| 日韩天堂av| 欧美在线你懂的| 最新亚洲激情| 久久久久久一区二区三区| 欧美日韩在线三区| 伊人久久成人| 亚洲欧美国产毛片在线| 亚洲福利视频免费观看| 性久久久久久久久| 欧美视频一区在线| 亚洲精品免费一二三区| **网站欧美大片在线观看| 亚洲日本欧美日韩高观看| 亚洲女女女同性video| 亚洲理伦电影| 亚洲视频www| 欧美成人午夜免费视在线看片 | 欧美在线视频一区二区| 欧美激情精品久久久久| 狠狠色狠狠色综合日日小说| 亚洲欧美制服另类日韩| 亚洲日本黄色| 欧美精品v日韩精品v国产精品 | 红桃视频欧美| 欧美中文在线视频| 亚洲一区在线看| 日韩视频一区二区在线观看 | 亚洲综合三区| 亚洲日本成人女熟在线观看| 久久久久国产精品www| 国产午夜亚洲精品理论片色戒| 亚洲欧美在线看| 亚洲欧美久久久| 国产精品一区二区三区观看| 先锋影音国产一区| 欧美一区二区三区男人的天堂| 国产精品视频网| 欧美在线一二三区| 亚洲欧美在线看| 国产精品试看| 久久gogo国模裸体人体| 欧美在线视频不卡| 国产一区亚洲一区| 久久久久久精| 久久综合久久美利坚合众国| 91久久久久久久久| 亚洲国产欧美一区二区三区同亚洲 | 亚洲视频二区| 亚洲午夜精品一区二区| 国产午夜精品麻豆| 牛夜精品久久久久久久99黑人| 久久天天综合| 日韩午夜电影在线观看| 9人人澡人人爽人人精品| 国产精品一区免费在线观看| 久久久精品久久久久| 欧美~级网站不卡| 亚洲视频福利| 久久久国际精品| 一本久久精品一区二区| 亚洲一区二区精品在线观看| 国内外成人在线| 亚洲国产综合在线看不卡| 国产精品r级在线| 久久人人爽爽爽人久久久| 欧美激情按摩在线| 久久久精品国产免费观看同学 | 欧美在线观看一区二区三区| 亚洲大片av| 亚洲一区二区三区四区在线观看 | 在线一区二区视频| 国产一区在线看| 亚洲美女视频在线免费观看| 国产欧美在线观看| 亚洲毛片av| 香蕉成人久久| 一本久道久久综合婷婷鲸鱼 | 欧美激情一区二区三区在线视频观看 | 国产精品美女久久久久aⅴ国产馆| 一区二区三区蜜桃网| 亚洲视频免费观看| 怡红院av一区二区三区| 一二三区精品| 亚洲激情在线观看| 亚洲欧美一区二区三区极速播放| 亚洲国产日韩欧美| 亚洲字幕一区二区| 亚洲视频精选在线| 免费久久99精品国产| 欧美在线中文字幕| 欧美性片在线观看| 亚洲国产天堂网精品网站| 好吊色欧美一区二区三区视频| 99精品国产福利在线观看免费| 激情久久五月| 欧美怡红院视频一区二区三区| 亚洲素人在线| 欧美精品一区二区视频| 欧美成年人网| 在线成人激情黄色| 欧美在线中文字幕| 久久国产一区二区三区| 国产精品永久| 亚洲欧美另类久久久精品2019| 亚洲一区日韩在线| 欧美日韩在线另类| 亚洲美女毛片| 亚洲综合二区| 国产精品亚洲不卡a| 亚洲一区二区三区四区五区黄 | 国产日韩欧美精品| 亚洲一区二区三区精品视频 | 91久久久久| 美女尤物久久精品| 亚洲大片免费看| 最新成人在线| 欧美激情一区二区| 亚洲美女视频| 亚洲已满18点击进入久久| 欧美四级伦理在线| 亚洲一区二区成人在线观看| 欧美亚洲一区二区在线| 国产日韩一区| 免费国产自线拍一欧美视频| 亚洲国产精品成人综合| 一本久道久久综合中文字幕| 国产精品高潮呻吟久久av无限| 亚洲综合二区| 亚洲第一在线综合网站| 乱码第一页成人| 91久久国产综合久久| 亚洲一区二区综合| 国产一区二区三区高清在线观看| 久久国产精品久久久| 欧美韩日亚洲| 亚洲一区激情| 激情综合电影网| 欧美日韩精品免费在线观看视频| 亚洲免费在线视频| 亚洲第一精品在线| 午夜精品久久久久| 亚洲国产专区校园欧美| 国产精品高潮呻吟久久| 久久免费视频网| 一本大道久久精品懂色aⅴ| 羞羞视频在线观看欧美| 亚洲韩国青草视频| 国产精品美女久久久久av超清| 久久九九99| 中文日韩欧美| 亚洲风情亚aⅴ在线发布| 午夜在线播放视频欧美| 亚洲国产日本| 国产日韩欧美综合| 欧美日韩1080p| 欧美一区二区视频在线观看2020| 欧美黄色日本| 老司机精品久久| 亚洲愉拍自拍另类高清精品| 在线播放亚洲一区| 国产精品中文字幕欧美| 欧美精品在线观看| 久久日韩精品| 欧美与黑人午夜性猛交久久久| 亚洲裸体在线观看| 欧美高清在线精品一区| 久久成人精品电影| 亚洲综合欧美| 一区二区三区国产| 亚洲国产精品专区久久| 好吊成人免视频| 国产在线视频欧美| 国产女主播一区二区| 国产精品免费观看视频| 欧美日韩中文| 欧美日韩激情小视频|