1. 程式人生 > >類(繼承)的記憶體分配詳解

類(繼承)的記憶體分配詳解

1.基類與派生類的記憶體分配

派生類繼承基類       記憶體分配時,是在於基類物件不同的記憶體地址處,按基類的成員變數型別,開闢一個同樣的型別空間,但注意開闢後派生物件的空間,不是複製基類的成員的值,而是僅僅開闢那種成員型別的空間,未初始化時,裡面存在的數是不確定的        然後派生類自己定義的成員變數是排在繼承的A類成員下面,如果派生類定義的變數名與基類相同,則此變數覆蓋掉繼承的基類同名變數,注意,覆蓋不是刪除,也 就是派生類中繼承自基類的成員變數依然存在,而且值也不發生變化。如果想用此繼承自基類的成員變數,則要加:: , 在成員函式中訪問時,直接用base::i,即可,用派生類的物件a訪問時,如果此繼承自基類的成員變數是物件可訪問的(Public型別),則用 a.base::i訪問之

Base base; Sub_class  sub; base.i; base.j 但是用物件訪問派生類的繼承自基類的成員變數時(假定public型別) 得用:  sub.Base::i sub.Base::j 直接用sub.i, 訪問的是子類自定義的i 如 以下程式

  1. class base{
  2. public:
  3.       int i;
  4.       int j;
  5. };
  6. class sub:public base{
  7.  public:
  8.          int i;
  9.          int j;
  10.     void p(){
  11.               cout<<base::i<<endl;
  12.               cout<<base::j<<endl;
  13.              }        
  14. };  
  15. base b;
  16. sub s;
  17. b.i=1;
  18. b.j=2;
  19. s.i=3;
  20. s.j=4;
  21. s.p();             //   輸出兩個不確定的數 4425598   4421322
  22. cout<<b.i<<endl;  //   1
  23. cout<<b.j<<endl;   //   2
  24. cout<<s.i<<endl;   //   3
  25. cout<<s.j<<endl;   //   4
  26. cout<<s.base::i<<endl;
  27. cout<<s.base::j<<endl;

輸出結果如下 4425598 4421322 1 2 3 4 4425598 4421322 Press any key to continue . . . 從派生類物件繼承的兩個基類變數的值和及基類物件兩個成員變數的值得比較看,足以驗證上述結論:  子類繼承的基類的成員,只是在另一個記憶體空間內開闢一個這種型別的成員變數,它的值並不是基類的值,編譯器只是負責把這一部分空間型別設定為與基類的型別相同  

2 普通類的記憶體分配

類所佔記憶體的大小是由成員變數(靜態變數除外)決定的,成員函式(這是籠統的說,後面會細說)是不計算在內的。

摘抄部分:

          成員函式還是以一般的函式一樣的存在。a.fun()是通過fun(a.this)來呼叫的。所謂成員函式只是在名義上是類裡的。其實成員函式的大小不在類的物件裡面,同一個類的多個物件共享函式程式碼。而我們訪問類的成員函式是通過類裡面的一個指標實現,而這個指標指向的是一個table,table裡面記錄的各個成員函式的地址(當然不同的編譯可能略有不同的實現)。所以我們訪問成員函式是間接獲得地址的。所以這樣也就增加了一定的時間開銷,這也就是為什麼我們提倡把一些簡短的,呼叫頻率高的函式宣告為inline形式(行內函數)。

(一) class CBase  {  };  sizeof(CBase)=1;

為什麼空的什麼都沒有是1呢? c++要求每個例項在記憶體中都有獨一無二的地址。//注意這句話!!!!!!!!!! 空類也會被例項化,所以編譯器會給空類隱含的新增一個位元組,這樣空類例項化之後就有了獨一無二的地址了。所以空類的sizeof為1。

 (二)

class CBase  {  int a;  char p;  };  sizeof(CBase)=8; 記得對齊的問題。int 佔4位元組//注意這點和struct的對齊原則很像!!!!! char佔一位元組,補齊3位元組

(三) class CBase  {  public:  CBase(void);  virtual ~CBase(void);  private:  int   a;  char *p;  };  再執行:sizeof(CBase)=12

C++ 類中有虛擬函式的時候有一個指向虛擬函式的指標(vptr),在32位系統分配指標大小為4位元組。無論多少個虛擬函式,只有這一個指標,4位元組。//注意一般的函式是沒有這個指標的,而且也不佔類的記憶體。

(四) class CChild : public CBase  {  public:  CChild(void);  ~CChild(void); 

virtual void test(); private:  int b;  };  輸出:sizeof(CChild)=16; 可見子類的大小是本身成員變數的大小加上父類的大小。//其中有一部分是虛擬函式表的原因,一定要知道

父類子類共享一個虛擬函式指標

(五)

#include<iostream.h>

class a {};

class b{};

class c:public a{

virtual void fun()=0;

};

class d:public b,public c{};

int main()

{

cout<<"sizeof(a)"<<sizeof(a)<<endl;

cout<<"sizeof(b)"<<sizeof(b)<<endl;

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

cout<<"sizeof(d)"<<sizeof(d)<<endl;

return 0;}

程式執行的輸出結果為:

sizeof(a) =1

sizeof(b)=1

sizeof(c)=4

sizeof(d)=8

前三種情況比較常見,注意第四種情況。類d的大小更讓初學者疑惑吧,類d是由類b,c派生邇來的,它的大小應該為二者之和5,為什麼卻是8 呢?這是因為為了提高例項在記憶體中的存取效率.類的大小往往被調整到系統的整數倍.並採取就近的法則,裡哪個最近的倍數,就是該類的大小,所以類d的大小為8個位元組.

總結:

空的類是會佔用記憶體空間的,而且大小是1,原因是C++要求每個例項在記憶體中都有獨一無二的地址。

(一)類內部的成員變數:

  • 普通的變數:是要佔用記憶體的,但是要注意對齊原則(這點和struct型別很相似)。
  • static修飾的靜態變數:不佔用內容,原因是編譯器將其放在全域性變數區。

(二)類內部的成員函式:

  • 普通函式:不佔用記憶體
  • 虛擬函式:要佔用4個位元組,用來指定虛擬函式的虛擬函式表的入口地址。所以一個類的虛擬函式所佔用的地址是不變的,和虛擬函式的個數是沒有關係的。