天天看點

(C++對象模型):對象結構的發展和演化對象結構的發展和演化

目錄

對象結構的發展和演化

歸納

案例探究

總結類對象大小的組成

對象結構的發展和演化

  • 經過多年,c++對象模型逐漸建立起來
  • ①非靜态的成員變量(普通成員變量)  跟着類對象走(存在對象内部),也就是每個類對象都有自己的成員變量;
class A {
public:
    int a = 100; //非靜态成員變量(普通成員變量)
};

int main()
{
    A aobj;
    int ilen = sizeof(aobj);
    std::cout << ilen << std::endl;

    return 0;
}
           
(C++對象模型):對象結構的發展和演化對象結構的發展和演化
  • ② 靜态成員變量跟對象沒有什麼關系,是以肯定不會儲存在對象内部,是儲存在對象外面(表示所占用的記憶體空間和類對象無關)的。
class A {
public:
    static int a; //靜态成員變量是跟着類走的;
    static int b;
};

int main()
{
    A aobj;
    int ilen = sizeof(aobj);
    std::cout << ilen << std::endl;

    return 0;
}
           
  • 輸出為1;
  • ③ 成員函數:不管靜态的還是非靜态,全部都儲存在類對象之外。是以不管幾個成員函數,不管是否是靜态的成員函數,對象的sizeof的大小都是不增加的;
class A {
public:
    static void sfunc() {}; //靜态成員函數
    void myfunc() {}; //普通成員函數
};  

int main()
{
    A aobj;
    int ilen = sizeof(aobj);
    std::cout << ilen << std::endl;

    return 0;
}
           
  • 輸出結果為1;
  • ④ 虛函數:不管幾個虛函數,sizeof()都是多了4個位元組
class A {
public:
    virtual void myfunc1() {};
    virtual void myfunc2() {};
    virtual void myfunc3() {};
};  


int main()
{
    A aobj;
    int ilen = sizeof(aobj);
    std::cout << ilen << std::endl;

    return 0;
}
           
  • 輸出結果為4;
  • 上述案例小結:
    • 類裡隻要有一個虛函數(或者說至少有一個虛函數),這個類 會産生一個 指向 虛函數 的指針。
      • 有兩個虛函數,那麼這個類 就會産生兩個指向虛函數的指針。
    • 類本身  指向虛函數的 指針(一個或者一堆)要有地方存放,存放在一個表格裡,這個表格我們就稱為“虛函數表(virtual table【vtbl】)”;
    • 這個虛函數表一般是儲存在可執行檔案中的,在程式執行的時候載入到記憶體中來。
    • 虛函數表是基于類的,跟着類走的;
    • 說說類對象,這四個位元組的增加,其實是因為虛函數的存在;因為有了虛函數的存在,導緻系統往類對象中添加了一個指針,這個指針正好指向這個虛函數表,很多資料上把這個指針叫vptr
      • 這個vptr的值由系統在适當的時機(比如構造函數中通過增加額外的代碼來給值);

歸納

  • 對于類中:
    • (1)靜态資料成員不計算在類對象sizeof()内;
    • (2)普通成員函數和靜态成員函數不計算在類對象的sizeof()内
    • (3)虛函數不計算在類對象的sizeof()内,但是虛函數會讓類對象的sizeof()增加4個位元組以容納虛函數表指針。
    • (4)虛函數表[vtbl]是基于類的(跟着類走的,跟對象沒關系,不是基于對象的);
    • (5)如果有多個資料成員,那麼為了提高通路速度,某些編譯器可能會将資料成員之間的記憶體占用比例進行調整。(記憶體位元組對齊)
class A {
public:
    char a; //1位元組
    int b;  //4位元組
};

int main()
{
    A aobj;
    int ilen = sizeof(aobj);
    std::cout << ilen << std::endl;  //輸出為8  (考慮位元組對齊等因素)

    return 0;
}
           
  • (6)不管什麼類型指針char *p,nt *q;    該指針占用的記憶體大小是固定的
int ilen2 = sizeof(char *);  //4位元組
int ilen3 = sizeof(int *);  //4位元組
           

案例探究

class myobject
{
public:
	myobject() {};//構造函數
	virtual ~myobject() {}; //析構函數       4位元組
	float getvalue() const //普通成員函數
	{
		return m_value;
	}

	static int s_getcount() //靜态成員函數 
	{
		return ms_scount;
	}

	virtual void vfrandfunc() {}; //虛函數 

protected:
	float m_value; //普通成員變量     4位元組
	static int ms_scount; //靜态成員變量
};

int main()
{
	myobject obj;
	int ilen = sizeof(obj);
	std::cout << ilen << std::endl; //8位元組,成員變量m_value占4位元組,虛函數表指針占4位元組。return 0;
}
           
  • 圖解上述代碼
(C++對象模型):對象結構的發展和演化對象結構的發展和演化

總結類對象大小的組成

  • ① 非靜态成員變量所占的記憶體總量以及這些成員變量之間位元組對齊所額外占用的記憶體;
  • ② 若有虛函數,則會産生虛函數表指針(vptr)。
  • 當然,如果類之間是多重繼承關系。并且每個父類都有虛函數,情況不同,後續探讨。

繼續閱讀