亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C++ > 正文

C++ 多重繼承和虛擬繼承對象模型、效率分析

2020-01-26 15:23:59
字體:
來源:轉載
供稿:網友

一、多態

C++多態通過繼承和動態綁定實現。繼承是一種代碼或者功能的傳承共享,從語言的角度它是外在的、形式上的,極易理解。而動態綁定則是從語言的底層實現保證了多態的發生――在運行期根據基類指針或者引用指向的真實對象類型確定調用的虛函數功能!通過帶有虛函數的單一繼承我們可以清楚的理解繼承的概念、對象模型的分布機制以及動態綁定的發生,即可以完全徹底地理解多態的思想。為了支持多態,語言實現必須在時間和空間上付出額外的代價(畢竟沒有免費的晚餐,更何況編譯器是毫無感情):

1、類實現時增加了virtual table,用來存放虛函數地址;
2、類對象中增加了指向虛函數表的指針vptr,以提供runtime的鏈接;
3、在類繼承層次的構造函數中重復設定vptr的初值,以期待指針指向對應類的virtual table;
4、在類繼承層次的析構函數中重復還原vptr的初值;
5、多態發生時(base class指針調用虛函數)需要通過vptr和virtual table表調用對應函數實體,增加了 一層間接性。
第1、2兩點是多態帶來的空間代價,后面三點則是時間效率上的代價。

二、多重繼承和虛擬繼承

多重繼承具有多個base class,有別于單一繼承(提供了一種“自然多態”形式)。單一繼承中,基類和派生類具有相同的內存地址,它們之間的轉換十分自然不需要編譯器的介入。但如果基類中沒有虛函數而派生類中有,單一繼承的自然多態被打破。這種情況下,派生類轉換為基類需要編譯器的介入,用以調整this指針地址。多重繼承的對象模型較單一繼承復雜,根源在于derived class objects和其第二或后繼的base class objects之間的“非自然”關系 ,這一點可以從下面的對象模型中看到。派生類和基類之間的非自然多態引起了一個嚴重的問題(在虛擬繼承中也存在):derived class和第二或后繼base class之間的轉換(不論是對象間的直接轉換或者經由其所支持的virtual function機制做轉換)需要調整this指針的地址,以使其指向完整正確的class object 。
虛擬繼承是一種機制,類通過虛繼承指出它所希望共享虛基類的狀態,虛基類在派生層次中只有一份實體。相比多重繼承,虛擬繼承的難點在于既要識別出相同的對象部分又要維持基類和派生類之間的多態關系 。通常情況下,實現虛擬繼承時編譯器將對象分割為一個不變局部和一個共享局部 。不變局部中的數據,不管后繼如何衍化,總是擁有固定的offset,所以這一部分數據可以被直接存取。至于共享局部,所表現的就是virtual base class subobject。這一部分的數據,其位置會因為每次的派生操作而有變化,所以它們只能被間接存取 。各家編譯器實現技術之間的差異在于間接存取方法不同。一般的策略就是先安排好派生類的不變部分,然后建立共享部分。虛擬繼承base class和derived class之間非自然的多態關系,它們之間相互轉換時需要對this指針地址進行調整。由于對virtual base class的支持,虛擬繼承帶來了額外的負擔和模型復雜性。

三、多重繼承和虛擬繼承對象模型

造成多重繼承和虛擬繼承較普通單一繼承復雜、效率低的本質在于 對象模型內存分布的差異, 這一點從第二部分分析也可以看到。下面示例對比列出了普通單一繼承、多重繼承以及虛擬繼承的對象模型。需要說明的是:C++標準中并沒有強制規定base class members和derived class members之間的次序關系,理論上可以自由安排之,但實際上大多數編譯器都會基類成員放在前面,但虛擬繼承除外。下面也是這種策略,同時把vptr作為類的第一個成員。

基類Base1、Base2以及派生類DerivedSingle、DerivedMulti類定義如下:

class Base1{public:  Base1(void);  ~Base1(void);  virtual Base1* clone()const;protected:  float data_Base1;};
class Base2{public:  Base2(void);  ~Base2(void);  virtual void mumble();  virtual Base2* clone()const;protected:  float data_Base2;};
class DerivedSingle: public Base1{public:  DerivedSingle(void);  virtual ~DerivedSingle(void);  virtual DerivedSingle* clone() const;protectd:  float data_DerivedSingle;};
class DerivedMulti :public Base1, public Base2{public:  DerivedMulti(void);  virtual ~DerivedMulti(void);  virtual DerivedMulti* clone() const;protected:  float data_DerivedMulti;};

對象模型如下,虛擬繼承和單一繼承類結構相同,只是繼承改成了虛擬繼承。

單一繼承

多重繼承:

虛擬繼承:

為了保證memberwise復制的正確性(否則基類子對象復制給派生類時會發生錯誤),C++中保證“基類子對象在派生類中的原樣性 ”。

單一繼承的對象模型呈現了一種“自然多態”的形式,基類和派生類之間的轉換十分自然簡單。然而多重繼承有多個基類,對象有多個vptr指針,對于第二個或后繼基類和派生類之間的轉換需要地址調整,以指向完整的基類子對象。

虛擬繼承中,為了記住和共享虛擬基類,需要在類中添加指向該基類的指針。從上面的虛擬繼承對象模型中可以看到,雖然和單一繼承有相同的類層次結構,但虛擬繼承打破了單一繼承的“自然多態”形式,基類和派生類之間的轉換需要調整this指針的地址。如果是虛擬多重繼承,則虛擬基類/后繼基類和派生類之間的轉換需要this指針地址調整 。

一般規則,多重繼承經由指向“第二個或者后繼base class”的指針(引用)來調用derived class virtual function,該操作所連帶的“必要的this指針調整”操作,必須在執行期完成,也就是說offset的大小、以及吧offset加到this指針上頭的那一小段程序代碼,必須有編譯器在某個地方插入。為了實現this指針調整引入thunk技術,所謂thunk是一小段assembly代碼,用來以適當的offset值調整this指針,并跳到virtual函數去。Thunk技術允許virtual table slot繼續內含一個簡單的指針,因此多重繼承不需要額外任何空間上的額外負擔。Slots中的地址可以直接指向virtual function,也可以指向一個相關的thunk(如果需要調整this指針)。調整this指針的第二個額外負擔就是,由于兩中不同的可能:(1)經由derived class(或者第一個base class)調用,(2)經由第二個(或者后繼)base class調用,同一個函數在virtual table中可能需要多筆對應的slots。并且在第二個或者后繼base class中的虛函數表保存的是thunk代碼地址。

四、 效率

通過上面第三部分的分析,多重繼承和虛擬繼承對象模型的較單一繼承復雜的對象模型 ,造成了成員訪問低效率, 表現在兩個方面:對象構建時vptr的多次設定,以及this指針的調整。對于多種繼承情況的效率比較如下:

情形 Vptr 設定 Data member 訪問 virtual Function member 訪問 效率分析
單一繼承 no vptr 指針/引用/對象訪問效率相同 直接訪問 效率較高
單一繼承 一次 指針/引用/對象訪問效率相同 通過vptr和vtable訪問 多態的引入,帶來了設定vptr和間接訪問虛函數等效率的降低
多重繼承 多次 指針/引用/對象訪問效率相同 通過vptr和vtable訪問,通過第二或者后繼base類指針訪問需要調整this指針 除了單一繼承效率降低的情形,調整this指針也帶來了效率的降低
虛擬繼承 多次 對象/指針/應用訪問效率較低 通過vptr和vtable訪問,訪問虛基類需要調整this指針 除了單一繼承效率降低的情形,調整this指針也帶來了效率的降低

多態中的data member訪問

    考察多態中幾種繼承情形的data member成員訪問效率的關鍵是:members的offset位置在編譯期是否能夠確定。 如果訪問的成員在編譯期就可以確定下offset位置,不會帶來額外的負擔。

    理論上針對上面的繼承類型,通過類對象訪問,效率完全一樣,因為成員在類中的位置在編譯期是可以確定的。通過引用或者指針訪問,除了一種情形,上面的繼承類型效率也完全相同 。例外情形是:通過指針和引用訪問虛擬基類的數據成員。因為虛擬基類在不同的繼承層次中,其offset位置是變化的,并且無法通過指針或者引用類型確定指針指向對象的真實類型,所以編譯期無法確定offset位置,只能在運行期通過類型信息確定。

    實際上具體繼承(非virtual繼承)并不會增加空間或者存取時間上的額外負擔,但是虛擬繼承的“間接性”壓抑了“把所有運算都移往緩存器執行”的優化能力,即使通過類對象訪問編譯器也會像對待指針一樣(目前是,編譯器都沒能識別出對“繼承而來的data member”的存取是通過一個非多態對象,因而不需要執行期的間接存?。?效率令人擔心。但間接性并不會嚴重影響非優化程序的執行效率,各類型繼承效率差別不大。一般來說,virtual base class最有效的運用形式:一個抽象的virtual base class,沒有任何data   members。

多態中的function member訪問

     在C++中,nonmember/static member/nonstatic member函數都被轉化為完全相同的形式(通過managling命名處理),所以它們的效率完全相同。

     如果是通過引用和指針調用虛函數,效率將會降低,這是由C++多態性質決定的。而多重繼承和虛擬繼承中虛函數的調用比單一繼承的效率更低。這個從上面表格可以清楚的看出來:this指針調(比如通過thunk技術調整)和多次初始化vptr。當然,請記?。和ㄟ^對象訪問虛函數和訪問非虛成員函數效率是一樣的。在調用虛函數而又不需要多態的情況下,可以明確地調用該函數實體:類名::函數名,壓制由于虛擬機制而產生的不必要的重復調用操作。

this指針地址調整
       多重繼承和虛擬繼承中this指針調整使得這兩種繼承效率降低,實際編程時應該有所警惕。下面列出常見的需要調整this指針的情形:

      1、new 派生類給第二(后繼)個基類指針或通過第二(后繼)base class調用派生類虛析構函數

      必須調整Derived對象的地址,以使其指向Base2 subobject對象。當刪除基類指向的對象時必須再一次調整,使其指向Derived對象的起始地址,然而這個調整只能在執行期完成,在編譯時無法確定指針指向的對象類類型。

      下次你看到這種情況不要好奇:pBase2不等于pDerived。      

Derived* pDerived = new Derived;Base2* pBase2 = pDerived; // Base2為Derived的第二個基類pBase2 != pDerived;    // 兩者不等

 2、通過派生類指針調用第二或后繼base class擁有的虛函數

      如果想正確調用必須在編譯時調整派生類指針,以指向后繼base subobject調用正確的虛函數。由上面的模型圖可以看到:如果通過派生類指針調用mumble函數,而mumble函數只存在于后繼類的虛函數表中,故必須調整之。

      3、后繼base class指針調用返回derived class type的虛函數并且賦值給另一后繼base class指針時

      示例如下:

Base2* pb1 = new Derived;  // 調整指針指向base2 clss子對象Base2* pb2 = pb1->clone(); // pb1被調整至Derived對象的地址,產生新的對象,再次調整對象指針指向base2基類子對象,賦值給pb2。

 記住:Base class指針一定得指向一個完整的與自身類型相同的對象或者子對象地址,不滿足這個條件的情形都需要this指針的調整。

     詳細知識請參考:《Inside The C++ Object Model》。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久精品国产久精国产思思| 欧美成aaa人片免费看| 欧美视频不卡中文| 久久久av免费| 亚洲第一区中文99精品| 日韩大胆人体377p| 97成人精品区在线播放| 5566日本婷婷色中文字幕97| 久久久999精品免费| 青青草99啪国产免费| 成人h视频在线观看播放| 亚洲色图偷窥自拍| 国产精品福利在线观看网址| 亚洲黄色在线看| 日韩中文字幕国产| 国产不卡在线观看| 国产网站欧美日韩免费精品在线观看| 最新国产精品亚洲| 2019av中文字幕| 国产欧美日韩91| 美女精品久久久| 精品露脸国产偷人在视频| 色偷偷888欧美精品久久久| 欧美亚州一区二区三区| 91黑丝在线观看| 日韩在线免费视频观看| 国产日韩欧美在线| 国产精品流白浆视频| 日韩一区视频在线| 欧美视频一区二区三区…| 都市激情亚洲色图| 国产精品极品尤物在线观看| 日韩中文字幕在线视频播放| 欧美成人合集magnet| 亚洲色图欧美制服丝袜另类第一页| 亚洲在线观看视频| 日韩精品亚洲精品| 欧美日韩国产二区| 亚洲国产精品人人爽夜夜爽| 亚洲国产精品高清久久久| 欧美老女人性视频| 欧美在线激情网| 久久视频在线观看免费| 日韩av手机在线观看| 韩国视频理论视频久久| 亚洲色图国产精品| 亚洲成人精品在线| 亚洲伦理中文字幕| 国产在线拍偷自揄拍精品| 日韩欧美在线第一页| 欧美夫妻性生活xx| 日韩国产精品一区| 国产成人avxxxxx在线看| 精品中文字幕视频| 日韩av电影在线免费播放| 亚洲精品v天堂中文字幕| 97av视频在线| 亚洲a在线观看| 国产丝袜一区二区| 欧美壮男野外gaytube| 亚洲国产成人av在线| 亚洲一区二区三区sesese| 亚洲人免费视频| 国产精品va在线播放我和闺蜜| 亚洲精品美女在线| 久久精品国产久精国产思思| 国产日产欧美a一级在线| 伊人亚洲福利一区二区三区| 久久久噜噜噜久久中文字免| 91精品国产亚洲| 热99在线视频| 中国人与牲禽动交精品| 2019中文字幕全在线观看| 国产精品青青在线观看爽香蕉| 亚洲欧美另类人妖| 欧美亚洲另类在线| 亚洲精品在线观看www| 国产精品入口尤物| 欧美日韩国产一区二区| 国产日韩综合一区二区性色av| 7777精品久久久久久| 中文字幕在线成人| 国产精品一区二区久久精品| 亚洲综合av影视| 欧美精品在线观看91| 亚洲欧美成人精品| 日韩专区在线播放| 亚洲毛片在线观看| 日本一区二三区好的精华液| 国产精品福利网站| 亚洲专区在线视频| 高跟丝袜一区二区三区| 在线观看日韩视频| 欧美成人性色生活仑片| 国内精品小视频| 午夜精品美女自拍福到在线| 高潮白浆女日韩av免费看| 亚洲一区二区三区视频播放| 狠狠综合久久av一区二区小说| 国产精品久久久久久av下载红粉| 91免费精品国偷自产在线| 亚洲精品视频在线播放| 色妞欧美日韩在线| 亚洲免费电影一区| 欧美视频免费在线| 亚洲成年人影院在线| 亚洲精品久久久久久久久久久久久| 国产精品偷伦免费视频观看的| 亚洲天堂av综合网| 这里只有精品丝袜| 91久久精品国产91性色| 2024亚洲男人天堂| 亚洲欧美一区二区三区情侣bbw| 亚洲欧洲免费视频| 国产精品福利在线观看网址| 日韩在线视频免费观看高清中文| 欧美专区在线视频| 成人网在线免费观看| 午夜精品久久17c| 国产精品美乳一区二区免费| 久久视频中文字幕| 精品久久久免费| 狠狠躁18三区二区一区| 欧美国产日韩一区二区在线观看| 成人日韩av在线| 色与欲影视天天看综合网| 亚洲精品成人久久电影| 日韩有码在线播放| 久久综合久久美利坚合众国| 91禁外国网站| 日本不卡高字幕在线2019| 久久久免费精品视频| 欧美极品少妇xxxxⅹ裸体艺术| 一区二区欧美日韩视频| 国产一区私人高清影院| 久久久精品一区二区三区| 欧美日韩高清区| 久久久久久网址| 中文字幕亚洲色图| 国产精品在线看| 久久影视电视剧免费网站| 日韩av电影手机在线| 成人中文字幕+乱码+中文字幕| 久久久国产在线视频| 国产精品91视频| 色婷婷综合成人av| 亚洲欧美日韩精品久久亚洲区| 伊人久久免费视频| 精品久久久久久中文字幕| 欧美极品少妇xxxxⅹ喷水| 久久精品在线视频| 亚洲黄色片网站| 日韩高清不卡av| 亚洲成色www8888| 午夜精品久久久久久99热| 色综合五月天导航| 岛国av一区二区| 高清欧美性猛交xxxx| 成人有码视频在线播放| 成人欧美一区二区三区在线湿哒哒| 国产999精品视频| 日韩精品一区二区视频| 欧美精品久久久久久久久| 久久久女女女女999久久|