c++ devived object model

來源:互聯網
上載者:User

標籤:特性   off   bit   mod   return   插入   父類   取數   rtu   

單一虛函數繼承

class A
{
public:
virtual int foo( ) { return val ; }
virtual int funA( ) {}
private:
int val ;
char bit1 ;
} ;

class B : public A
{
public:
virtual int foo( ) { return bit2; }
virtual int funB( ) {}
private:
char bit2 ;
};

 

 

 

 

 

一個class只要有一個虛函數,那麼每一個class object被安插上一個由編譯器內部產生的指標,指向該表格(virtual table)。

virtual table 的第一項是表示class的類型。

因為,基類指標的特殊性,它可以指向基類對象,也可以指向衍生類別對象。故:ptr->foo( ) ;這種調用,我們需要知道ptr所指對象的真實類型。(就算不知道ptr所指對象的類型,也可以正確調用fun函數,但是由於fun函數有編譯器插入的this指標,this指標要與ptr指向的對象地址正確對應,以正確訪問對象中的成員變數,但ptr中卻沒有這樣的資訊)

virtual table 之後的表格是class中的每個虛函數地址。

 

一個class只會有一個virtual table。衍生類別的virtual table是在基類的virtual table上增加,修改的。

衍生類別中的虛函數會改寫(overriding)與基類中同名且參數相同的虛函數,把virtual table表中相應的基類虛函數地址改寫為相應衍生類別虛函數的地址。

 

以上工作都是由編譯器完成的。執行期要做的就是在特定的virtual table表項中啟用相應的虛函數,然後根據virtual table首項的類型資訊,正確執行此虛函數。

 

例如:ptr->foo( ) ;

一般而言,我並不知道ptr所指對象的真正類型。然而我知道。經由ptr可以存取到該對象的virtual table。

雖然我不知道哪一個foo( )實體會被調用,但我知道每一個foo( )函數的地址都放在虛表的第二項。

故:根據以上資訊,編譯器可以將該調用轉化為:

( *ptr->vptr[1] )( ptr ) ;

 

 

【注意:】基類指標雖然可以指向衍生類別,但是它實際上指向的是衍生類別中的基類部分。(這就不違反指標的特性了,指標類型與其指向範圍是一致的)故基類指標不能訪問衍生類別的成員。(但基類指標可以通過訪問衍生類別的虛函數,間接操作衍生類別成員)

上面的記憶體配置圖驗證了這個例子,就是指的是指向衍生類別的指標,指標指向base class中沒有被devived class繼承的虛函數,或者指向devived class中重寫base class中的虛函數,

 

 

多重繼承

 

class A  {  public:      A() {}      virtual ~A() {}      virtual int foo( )  {   return  val ;  }      virtual int funA( ) {}  private:      int val ;      char bit1 ;  } ;    class B :  {  public:      B() {}      virtual ~B() {}      virtual int foo( )  {   return  bit2;  }      virtual int funB( ) {}  private:      char bit2 ;  };    class Derived : public A, public B  {  public:      Derived() {}      virtual ~Derived() {}      virtual int foo( )  {   return  bit3;  }      virtual int funDerived( ) {}  private:      char bit3 ;  };  

注意:在多重繼承下,若有n個基類,則衍生類別中有n個virtual table.

針對每一個virtual table,衍生類別對象中有對應的vptr。這些vptrs將在建構函式中被設立初值。

 

衍生類別的虛函數會覆蓋(改寫)其每個基類virtualtable中相應的虛函數索引值。

 

多重繼承最左端的基類,在衍生類別中作為主要實體,其virtualtable為主要表格,其它基類的virtual table為次要表格。

當你將Derived對象地址指定給一個Base1指標或Derived指標時,被處理的virtualtable是主要表格vptr_Base1。

故主要表格要包含Derived的所有虛函數(包括繼承得來的虛函數)。所以其中有Base1、Base2、Derived中的虛函數。

而其它次要表格中的項目數不變,只是有些虛函數的索引值被重寫。

 

涉及多重繼承的指標的轉換

多重繼承的問題主要發生於:衍生類別對象和其第二或後繼的基類對象之間的轉換。

【對一個多重派生對象,將其地址指定給“最左端(也就是第一個)”base class的指標,情況將和單一繼承時相同,因為二者都指向相同的起始地址。需付出的成本只有地址的指定操作而已。至於第二個或後繼的base class的地址指定操作,則需要將地址修改:加上(或減去)介於中間的base class subobject(s)大小】

例如:

Derived dObj ;

A* pA = &dObj ;

只需要簡單地拷貝地址就行了。

 

而:

Derived* pd ;

B* pB = pd ;

需要這樣的內部轉化:

//虛擬C++碼

pB = pd ? (B*)( (char*)pd + sizeof(A) ) : 0  ;

 

 

 

含虛繼承的多重繼承

class A {  } ;  class B : public virtual A {  } ;  class C : public virtual A {  } ;  class D : public B, public C { }; 

注意】class A { };實際上並不是空的,它有一個隱晦的1位元組,那是被編譯器安插進去的一個char。這是為了使得class A的對象得以在記憶體中配置獨一無二的地址。

當語言支援虛基類時,就會導致一些額外負擔。在衍生類別中會有一個額外指標,指標指向一個相關表格,表格中存放的或者是虛基類對象,或者是其位移量。

若虛基類為空白,則表格中存放的是虛基類對象(即一個安插位元組)

 

 

VC++編譯器的最佳化:

VC++特別對 空virtual baseclass做了處理。空虛基類的衍生類別中只有一個4位元組的指標。沒有安插char,也沒有位元組填充。

(因為安插char的目的是為了空類執行個體化的對象在記憶體中有地址,而現在其衍生類別對象中已經有個指標了,其可以取地址,故不需要安插char了)

注意:如果虛基類中有資料成員,則兩種編譯器(“有特殊處理者”和“無特殊處理者”)就會產生完全相同的物件版面配置。

 

2、虛基類有資料成員

例:

class A {public:     …private:    int x, y ;} ;class B : public virtual A {public:    …private:    int valB ;} ;class C : public virtual A {public:    …private:    int valC ;} ;class D : public B, public C {public:    …private:    int valD ;};

最終的衍生類別對象底部是共用虛基類的部分,衍生類別class B、class C部分的指標指向的虛函數表的前面增加了一項:offset(從對象的開頭算起,到共用虛基類部分的位元組數)

這樣可以快速地訪問到共用虛基類的成員。

 

問:

①為什麼虛繼承不像一般繼承那樣,把基類成員放在頂部,把新增衍生類別成員放在尾部?

我想是:因為在最後的多重繼承時,要求最終的衍生類別對象中只有一份基類成員,故最終衍生類別會從其各個父類中提取它們各自的派生資料成員。若按一般繼承那樣的物件模型,很容易找到父類中的衍生類別成員,但難以確定邊界,故難以提取資料。為了提取資料方便,把基類成員放在尾部。

 

②為什麼虛繼承的衍生類別中有兩個虛指標?

我想是:因為要實現多態。當基類指標指向衍生類別中的基類部分時,必須要有指向虛表的指標,才能實現多態。

 

注意:class D物件模型與一般多重繼承的衍生類別對象的布局相似,多重繼承最左邊的基類部分的虛表是“主要表格”。故class B、class C、class A部分的虛表各不相同。它們都是為了實現多態。

 

【編程風格】一般而言,virtual base class最有效一種運用形式是:一個抽象的virtual base class,沒有任何資料成員。

c++ devived object model

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.