類(繼承)的內存分配詳解

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個字節,用來指定虛函數的虛擬函數表的入口地址。所以一個類的虛函數所佔用的地址是不變的,和虛函數的個數是沒有關係的。
C++中的類所佔內存空間總結

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章