1. 程式人生 > >C++虛繼承與普通繼承的區別

C++虛繼承與普通繼承的區別

虛繼承的時候在子類的物件中會多出一個叫虛類指標的大小,有的資料說這個指標指向的記憶體裡面包含了該子類的偏移量和到基類的距離。但是我跟蹤過這段記憶體,發現裡面的資料沒有規律,也找不到更多的支撐材料,權且先知道子類的物件裡面會有這麼一個東西吧。

先總結虛擬繼承中比較特殊的地方,希望能夠對大家有所幫助:

虛繼承時子類的虛擬函式不再是新增到父類部分的虛表中,而在普通的繼承中確實直接新增到父類的虛表中,這就意味著如果虛繼承中子類父類都有各自的虛擬函式,在子類裡面就會多出一個虛表指標,而普通的繼承卻不會這樣。程式碼說明:

class B
{
public:
 char b[3];
public:
 virtual void bb()
 {
  cout<<"B"<<endl;
 }
};

class C:public virtual B
{
public:
 char c[3];
public:
 virtual void cc()
 {
  cout<<"C"<<endl;
 }
};

int main()
{
 C c;

 c.c[0]=1;
 c.c[1]=2;
 c.c[3]=3;

 c.b[0]=4;
 c.b[1]=5;
 c.b[2]=6;

 C*ptr=&c;
 void (*PtrFun)();
 int *p,*p2;

 memcpy(&p,&ptr,4);//使得p指向c的地址
 memcpy(&p2,p,4);  //使得p2指向虛表的地址
 memcpy(&PtrFun,p2,4);
 PtrFun();//呼叫C的虛擬函式表明前四個位元組是C的虛擬函式表的位置

 p+=1;
 //跳過C的虛類指標
 p+=1;
 //跳過C的陣列地址
 p+=1;

 memcpy(&p2,p,4);//定位到B的虛表地址
 memcpy(&PtrFun,p2,4);  //呼叫B的虛擬函式                 
 PtrFun();

 cout<<sizeof(c)<<endl;
}

普通繼承中子類中則不會多出虛類指標,子類也不會有自己單獨的虛擬函式列表,子類的虛擬函式會被嵌到基類部分的虛擬函式表的後面,程式碼如下:

int main()
{
 C c;

 c.c[0]=1;
 c.c[1]=2;
 c.c[3]=3;

 c.b[0]=4;
 c.b[1]=5;
 c.b[2]=6;

 C*ptr=&c;
 void (*PtrFun)();
 int *p,*p2;

 memcpy(&p,&ptr,4);//使得p指向c的地址
 memcpy(&p2,p,4);  //使得p2指向虛表的地址
 memcpy(&PtrFun,p2,4);
 PtrFun();//呼叫C的虛擬函式表明前四個位元組是C的虛擬函式表的位置

 p2+=1;
 memcpy(&PtrFun,p2,4);
 PtrFun();

 cout<<sizeof(c)<<endl;
}

從中我們不難發現虛擬繼承父類子類的記憶體排列方式也有很大的差別,普通繼承中父類在前,子類在後;虛擬繼承中先是子類部分的記憶體,接著再是父類的記憶體。對於虛擬繼承中的多次繼承就更奇葩了。

附上程式碼,大家應該很容易看明白其中的記憶體是如何分佈的:

#include <iostream>
using namespace std;

class A
{
public:
 char a[3];
public:
 virtual void ba()
 {
  cout<<"A"<<endl;
 }
};

class B:public virtual  A
{
public:
 char b[3];
public:
 virtual void bb()
 {
  cout<<"B"<<endl;
 }
};

class C:public virtual B
{
public:
 char c[3];
public:
 virtual void cc()
 {
  cout<<"C"<<endl;
 }
};

int main()
{
 C c;
 C*ptr=&c;
 void (*PtrFun)();
 int *p,*p2;

 memcpy(&p,&ptr,4);//使得p指向c的地址
 memcpy(&p2,p,4);  //使得p2指向虛表的地址
 memcpy(&PtrFun,p2,4);
 PtrFun();//呼叫C的虛擬函式表明前四個位元組是C的虛擬函式表的位置

 p+=1;
 //跳過C的虛類指標
 p+=1;
 //跳過C的陣列地址
 p+=1;

 memcpy(&p2,p,4);//我覺得應該是到B的地址了,但是確實到A的地址
 memcpy(&PtrFun,p2,4);                    //大概是將A放到了前面吧
 PtrFun();//呼叫A的虛擬函式,表明此時的p所儲存的是A的虛擬函式表的位置

 p+=1;
 //跳過A的陣列地址
 p+=1;

 memcpy(&p2,p,4);
 memcpy(&PtrFun,p2,4);
    PtrFun();//呼叫B的虛擬函式,表明此時的p所儲存的是B的虛表地址

 c.c[0]=1;
 c.c[1]=2;
 c.c[3]=3;

 c.b[0]=4;
 c.b[1]=5;
 c.b[2]=6;

 c.a[0]=7;
 c.a[1]=8;
 c.a[2]=9;
}

沒錯 C的記憶體是在前面,但是後面緊接著的不是B的記憶體而是C的記憶體,至於為什麼會這樣分配,這個我也不知道,希望知道的人可以指點一下。(普通繼承下是 C B A)



相關推薦

C++繼承普通繼承區別

虛繼承的時候在子類的物件中會多出一個叫虛類指標的大小,有的資料說這個指標指向的記憶體裡面包含了該子類的偏移量和到基類的距離。但是我跟蹤過這段記憶體,發現裡面的資料沒有規律,也找不到更多的支撐材料,權且先知道子類的物件裡面會有這麼一個東西吧。 先總結虛擬繼承中比較特殊的地方,

C++中繼承組合的區別

物件和類是C++中的重要內容,物件(Object)是類(Class)的一個例項(Instance)。面向物件設計的重點是類的設計,而不是物件的設計。對於C++程式而言,設計孤立的類是比較容易的,難的是正確設計基類及其派生類。這就和“繼承”(Inheritance)和“組合”(Composition)

c++單繼承繼承(包含虛擬函式繼承的對比)

先來個概念分析題: class Person { public: void Show() { cout<<"Person::"<<_name&l

c++】深入剖析虛擬繼承各種繼承關係中派生類內成員記憶體分佈情況及基類表的內容

概要 本文講述在VS2012環境下,採用程式碼和圖結合的方法,分析C++程式碼中不同繼承方式的物件模型,以及從彙編角度分析虛擬繼承編譯器生成的虛基類表裡的內容,不涉及虛擬函式。 繼承分類: 1.單繼承 一個子類只有一個直接父類 // 單繼承 工人類 繼承 人類 cl

繼承的概念及其作用,繼承一般繼承區別

虛繼承是多重繼承特有的概念,這裡需要明確的是,虛繼承與虛擬函式繼承是完全不同的概念。 虛繼承是為解決多重繼承而出現的,可以節省記憶體空間 舉例: 類c4繼承自類c2和類c3,類c2繼承自類c1,類c3頁繼承自類c1。這樣類c1就出現2次,我們可以通過虛繼承節省記憶體空間,

繼承派生的區別和聯絡

繼承與派生其實是同一過程從不同的角度看,我們將保持已有類的特性而構造新類的過程稱為繼承,說白了繼承的目的就是實現原來設計與程式碼的重用,希望儘量利用原有的類。然而當新的問題出現,原有程式無法解決或不能完全解決時,需要對原有程式進行改造,在已有類的基礎上新增自己的特性而產生新類的過程稱為派

python中的幾個高階問題詳解(__init__,裝飾器執行步驟,@staticmethod和@classmethod區別,單例模式,魔法方法,object繼承繼承區別

第一個問題,init 在定義一個類時,什麼時候用__init__函式,什麼時候不用,用不用有什麼區別? 首先__init__是為了初始化用的,但是初始化的時候不一定要用這個,直接定義也是可以的,比如 class A(object): test_a = '123' 而我們用__

C# 靜態變數普通變數的區別

靜態變數與普通變數的區別 全域性變數(外部變數)的說明之前再冠以static 就構成了靜態的全域性變數。全域性變數本身就是靜態儲存方式, 靜態全域性變數當然也是靜態儲存方式。這兩者在儲存方式上並無不同。這兩者的區別雖在於非靜態全域性變數的作用域是整個源程式, 當一個源程式

Spring bean 的繼承Java 繼承區別

      引言:最近在學習spring,感覺還是很有收穫的,順便整理了下知識點,以後沒事就來看看,學習學習...         Spring 中的bean 繼承與Java 中的繼承截然不同。前者是例項與例項之間引數值的延續,後者則是從一般到特殊的細化。前者是物件與物

C++中單繼承多重繼承下的虛擬函式表

轉自:http://www.cnblogs.com/Z465360621/articles/4561344.html 虛擬函式表,以及虛擬函式指標: 1)每個有虛擬函式的類都有自己的虛擬函式表,每個包含虛擬函式的類物件都有虛擬函式表指標。 2)對於多重繼承

Scala繼承Java的區別

在之前的筆記Java靜態屬性和方法的繼承問題中,通過具體的實驗證明,在子類中重寫父類的欄位時並沒有覆蓋父類的欄位,只是隱藏了父類的欄位。而在scala中則不同,scala子類的同名欄位會重寫且覆蓋父類的同名欄位,這裡做了個簡單實驗,並記錄下來。 Paren

C++類的繼承多重繼承的訪問控制

在前面的練習中我們一直在使用public的繼承方式,即共有繼承方式,對於protected和private繼承方式,即保護繼承與私有繼承方式我們並沒有討論。    對於單個類來說,討論保護繼承與私有繼承的區別意義是不大的,他們的區別只在多級繼承的情況中體現。    在這裡我

C++繼承菱形繼承

繼承是⾯向物件復⽤的重要⼿段。 通過繼承定義⼀個類,繼承是型別之間的關係建模,共享公有的東西,實現各⾃本質不同的東西。 我們類成員訪問限定符有三種: public(公有),protected(保護),private(私有) 我們的繼承關係也有三種: p

C#中StructClass的區別

而是 適用於 ack 定義 cts 多態 支持 關鍵字 for class和struct最本質的區別是class是引用類型,而struct是值類型,它們在內存中的分配情況有所區別。 什麽是class? class(類)是面向對象編程的基本概念,是一種自定義數據結構類型,通

java static 方法普通方法區別

對象 static 方法區 修改 需要 總結 資源 其他 一個  static 方法不需要實例化,靜態方法在JVM剛加載的時候就編譯過了.在程序的運行過程中隨時可以調用,不需要去實例化某個對象然後再去調用,可以直接用類名去調用,直到結束釋放內存,且靜態方法只能調用類靜態變量

c# 委托事件的區別

變量 del 另一個 ext 類型 編譯 擴展 hand 例子 委托與事件的區別 委托和事件沒有可比性,因為委托是數據類型,事件是對象(可以理解為對委托變量的封裝。),下面說的是委托的對象(用委托方式實現的事件)和(標準的event方式實現)事件的區別。事件的內部是用委托

C#中類結構的區別實例分析

類與結構 main bds nbsp 模擬鼠標 指向 img adding 區別 類與結構是C#程序設計中基本的數據類型,而初學者往往不能很好的分清二者之間的區別。本文就以附帶實例形式加以說明。具體如下: 一、基本概念: 類: 引用類型,存儲在堆中,棧中存儲引用地址

C++中compilebuild的區別

build com www ads upload 文件 font mil .com 我在前面的博文就提到了GCC編譯器工作的四個階段:預處理、編譯、匯編、鏈接。 感興趣的同學可以參考:http://www.cnblogs.com/mlgjb/p/7708007.html

C++:newmalloc的區別

函數 rim 自定義類 對象分配 文件 而是 計算 動態申請 成功 1.屬性 new/delete是操作符,是C++關鍵字,需要編譯器支持;malloc/free是庫函數,需要頭文件支持。 2.參數 使用new操作符動態分配內存時無需指定內存塊大小,編譯器會根據類型自行計算

C++:structclass的區別

c語言 類型 clas 對象 ++ 表示 模板類 沒有 c++ (1)C語言中struct與class的區別:struct只作為一種復雜數據類型定義的結構體,不能用於面向對象編程;C語言沒有class關鍵字。 (2)C++語言中struct與class的區別:對於成員訪