C++ 內存佈局:內存佈局基礎

1、在C++中,內存分成5個區,他們分別是堆、棧、自由存儲區、全局/靜態存儲區和常量存儲區。

    棧:在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有限。

    堆:就是那些由new分配的內存塊,他們的釋放編譯器不去管,由我們的應用程序去控制,一般一個new就要對應一個delete。如果程序員沒有釋放掉,那麼在程序結束後,操作系統會自動回收。

    自由存儲區:就是那些由malloc等分配的內存塊,他和堆是十分相似的,不過它是用free來結束自己的生命的。

    全局/靜態存儲區:全局變量和靜態變量被分配到同一塊內存中,在以前的C語言中,全局變量又分爲初始化的和未初始化的,在C++裏面沒有這個區分了,他們共同佔用同一塊內存區。

    常量存儲區:這是一塊比較特殊的存儲區,他們裏面存放的是常量,不允許修改。

2、C++中考慮內存佈局,主要是針對類內部的內存分佈。類的內存分佈涉及的內容比較多,我們先從最簡單的來看。

一、一個簡單的類,沒有繼承,沒有虛函數表

#include <iostream>
using namespace std;

class CBase
{
	//public
public:
	CBase()
	{

	}
};

class CBaseClass
{
	//private members
private:
	int nCount;

	//public members
public:

	//private member funcs
private:
	CBaseClass(const CBaseClass &base)
	{

	}

	CBaseClass &operator = (const CBaseClass& base)
	{
		return *this;
	}

	//public members
public:
	CBaseClass(int count = 0)
	{
		nCount = count;
	}
	~CBaseClass()
	{

	}
};

void main()
{
	CBase base;
	cout << "base Size" << sizeof(base) << endl;

	CBaseClass baseClass(10);
	cout << "baseClass Size" << sizeof(baseClass) << endl;

	cin.get();
    
}
上面代碼的輸出如下所示:


第一個CBase 的大小是1,雖然CBase只有一個構造函數,但是這個1並不是構造函數所佔據的內存空間。構造函數所佔據的內存空間在全局區。使用sizeof(CBase)所得到的大小,是編譯器優化的結果。
第二個CBaseClass 的大小是4,這是因爲CBaseClass 的內部含有一個int型的變量,該變量佔據4字節,所以輸出的值便是4字節。CBaseClass中那麼多的函數,不論是public還是private,均處於全局區。

上面這幅圖是在VS2013 X86(Win32 Debug)的條件下進行編譯的,得到上面的內存佈局圖,我們可以清除的看到每個類中的成員分佈。

二、簡單的繼承
在上面的代碼的基礎上進行擴展,得到如下的代碼
#include <iostream>
using namespace std;

class CBase
{
	//public
public:
	CBase()
	{

	}
};

class CBaseClass
{
	//private members
private:
	int nCount;

	//public members
public:

	//private member funcs
private:
	CBaseClass(const CBaseClass &base)
	{

	}

	CBaseClass &operator = (const CBaseClass& base)
	{
		return *this;
	}

	//public members
public:
	CBaseClass(int count = 0)
	{
		nCount = count;
	}
	~CBaseClass()
	{

	}
};

class CDerivedClass : public CBaseClass
{
	//private members:
private:
	int nDeriveCount;

	//public members
public:
	int nCurrentNum;

	//private member funcs
private:
	CDerivedClass(const CDerivedClass& derived)
	{

	}

	CDerivedClass & operator = (const CDerivedClass &derived)
	{
		return *this;
	}
	//public member funcs
public:
	CDerivedClass(int nDerived = 0)
	{
		nDeriveCount = nDerived;
		nCurrentNum = 0;
	}

};

void main()
{
	CBase base;
	cout << "base Size:" << sizeof(base) << endl;

	CBaseClass baseClass(10);
	cout << "baseClass Size:" << sizeof(baseClass) << endl;

	CDerivedClass derivedClass(12);
	cout << "derivedClass Size:" << sizeof(derivedClass) << endl;

	cin.get();
    
}

代碼輸出的內容如下:

我們可以看到baseClass 的大小任然是4,這也在一定的層次上說明了繼承不會影響基類的內存分佈。derivedClass 大小是12,這是因爲除了其本身的兩個int型的成員變量外,還有基類的成員變量。雖然基類的成員變量是私有的,derivedClass對這一私有成員沒有訪問權限,但是從繼承體系上來看,派生類繼承了基類的所有的內部關係,所以在計算派生類的大小時,需要考慮基類的大小。

上圖展示出了基類和派生類的內存分佈關係

三、多繼承
繼續修改上面的代碼,得到如下的內容
#include <iostream>
using namespace std;

class CBase
{
	//public
public:
	CBase()
	{

	}
};

class CBaseClass
{
	//private members
private:
	int nCount;

	//public members
public:

	//private member funcs
private:
	CBaseClass(const CBaseClass &base)
	{

	}

	CBaseClass &operator = (const CBaseClass& base)
	{
		return *this;
	}

	//public members
public:
	CBaseClass(int count = 0)
	{
		nCount = count;
	}
	~CBaseClass()
	{

	}
};

class CBaseClassNew
{
	//private members
private:
	int nCount;

	//public members
public:
	int nNewCount;
	//private member funcs
private:
	CBaseClassNew(const CBaseClassNew &base)
	{

	}

	CBaseClassNew &operator = (const CBaseClassNew& base)
	{
		return *this;
	}

	//public members
public:
	CBaseClassNew(int count = 0)
	{
		nCount = count;
	}
	~CBaseClassNew()
	{

	}
};

class CDerivedClass : public CBaseClass
{
	//private members:
private:
	int nDeriveCount;

	//public members
public:
	int nCurrentNum;

	//private member funcs
private:
	CDerivedClass(const CDerivedClass& derived)
	{

	}

	CDerivedClass & operator = (const CDerivedClass &derived)
	{
		return *this;
	}
	//public member funcs
public:
	CDerivedClass(int nDerived = 0)
	{
		nDeriveCount = nDerived;
		nCurrentNum = 0;
	}

};

class CMultiDerivedClass :public CDerivedClass, public CBaseClassNew
{
	//private members
private:
	int nMultiCount;

	//public members
public:
	int nNewMultiCount;

	//private member funcs
private:
	CMultiDerivedClass(const CMultiDerivedClass& derived)
	{

	}

	CMultiDerivedClass & operator = (const CMultiDerivedClass &derived)
	{
		return *this;
	}
	//public member funcs
public:
	CMultiDerivedClass(int nCount)
	{
		nMultiCount = nCount;
		nNewMultiCount = 0;
	}

	~CMultiDerivedClass()
	{

	}

};

void main()
{
	CBase base;
	cout << "base Size:" << sizeof(base) << endl;

	CBaseClass baseClass(10);
	cout << "baseClass Size:" << sizeof(baseClass) << endl;

	CDerivedClass derivedClass(12);
	cout << "derivedClass Size:" << sizeof(derivedClass) << endl;

	CMultiDerivedClass multiDerivedClass(5);
	cout << "multiDerivedClass Size:" << sizeof(multiDerivedClass) << endl;

	cin.get();
    
}

上面代碼的輸出如下:

其內存分佈如下圖所示:

從其內存分佈來看,可以很明瞭的知道多繼承CMultiDerivedClass的大小爲28,因爲上面有7個變量。多繼承具體的關係如下圖所示:

多繼承內存分佈總是從第一個繼承的類的成員開始分佈,然後是第二個,第三個....

在考慮類的內存佈局時,同樣需要考慮內存對齊等問題。這一方面,在這不多說,網上的資料很多。

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