C/C++_內存管理

C/C++ 內存分佈

在這裏插入圖片描述

  1. 棧又叫堆棧,非靜態局部變量/函數參數/返回值等等,棧是向下增長的。
  2. 內存映射段是高效的I/O映射方式,用於裝載一個共享的動態內存庫。用戶可使用系統接口創建共享共享內存,做進程間通信。
  3. 堆用於程序運行時動態內存分配,堆是可以向上增長的。
  4. 數據段–存儲全局數據和靜態數據。
  5. 代碼段–可執行的代碼 / 只讀常量。、

C語言中動態內存管理方式

malloc / calloc / realloc / free

C++內存管理方式

C 語言內存管理方式在 C++ 中可以繼續使用,但有些地方就無能爲力,因此C++又提出了自己的內存管理方式:通過 new 和 delete 操作符進行動態內存管理。

new 和 delete 操作內置類型

void Test() {
 // 動態申請一個int類型的空間
 int* ptr1 = new int;
 
 // 動態申請一個int類型的空間並初始化爲10
 int* ptr2 = new int(10);
 
 // 動態申請10個int類型的空間
 int* ptr3 = new int[10];
 
 delete ptr1;
 delete ptr2;
 delete[] ptr3;
}

注意: 申請和釋放單個元素的空間,使用new和delete操作符,申請和釋放連續的空間,使用new[] 和 delete[]

new和delete操作自定義類型

class Test {
	public:
	Test()
		: _data(0)
	{
		cout<<"Test():"<<this<<endl;
	}
	~Test() {
		cout<<"~Test():"<<this<<endl;
	}
 
private:
	int _data;
};
void Test1() {
	// 申請單個Test類型的空間
	Test* p1 = (Test*)malloc(sizeof(Test));
	free(p1);
	
	// 申請10個Test類型的空間
	Test* p2 = (Test*)malloc(sizoef(Test) * 10);
	free(p2);
}
void Test2() {
	// 申請單個Test類型的對象
	Test* p1 = new Test;
	delete p1;
	
	// 申請10個Test類型的對象
	Test* p2 = new Test[10];
	delete[] p2;
}

注意: 在申請自定義類型的空間時,new會調用構造函數,delete會調用析構函數,而malloc與free不會

operator new與operator delete函數

new 和 delete 是用戶進行動態內存申請和釋放的操作符,operator new 和 operator delete 是系統提供的全局函數,new 在底層調用 operator new 全局函數來申請空間,delete 在底層通過 operator delete 全局函數來釋放空間

operator new: 該函數實際通過 malloc 來申請空間,當 malloc 申請空間成功時直接返回;申請空間失敗,嘗試執行申請失敗的應對措施,如果應對措施用戶設置了,則繼續申請,否則拋異常
operator delete: 該函數最終是通過free來釋放空間的

new和delete的實現原理

內置類型
如果申請的是內置類型的空間,new 和 malloc,delete 和 free 基本類似,不同的地方是:new/delete 申請和釋放的是單個元素的空間,new[] 和 delete[] 申請的是連續空間,而且 new 在申請空間失敗時會拋異常,malloc會返回NULL

自定義類型
new的原理: 調用operator new函數申請空間。在申請的空間上執行構造函數,完成對象的構造

delete的原理: 在空間上執行析構函數,完成對象中資源的清理工作。調用operator delete函數釋放對象的空間

new[] delete[] 原理類似

定位new表達式(placement-new)

定位new表達式是在已分配的原始內存空間中調用構造函數初始化一個對象

使用場景:
定位 new 表達式在實際中一般是配合內存池使用。因爲內存池分配出的內存沒有初始化,所以如果是自定義類型的對象,需要使用 new 的定義表達式進行顯示調構造函數進行初始化

class Test {
public:
	Test()
	: _data(0)
	{
		cout<<"Test():"<<this<<endl;
	}
	~Test() {
		cout<<"~Test():"<<this<<endl;
	}

private:
	int _data;
};
void Test() {
	// pt現在指向的只不過是與Test對象相同大小的一段空間,還不能算是一個對象,因爲構造函數沒有執行
	Test* pt = (Test*)malloc(sizeof(Test));
	
	new(pt) Test; // 注意:如果Test類的構造函數有參數時,此處需要傳參
}

malloc/free和new/delete的區別

malloc/free和new/delete的共同點是:都是從堆上申請空間,並且需要用戶手動釋放。不同的地方是:

  1. malloc 和 free 是函數,new 和 delete 是操作符
  2. malloc 申請的空間不會初始化,new 可以初始化
  3. malloc 申請空間時,需要手動計算空間大小並傳遞,new只需在其後跟上空間的類型即可
  4. malloc 的返回值爲 void*, 在使用時必須強轉,new 不需要,因爲 new 後跟的是空間的類型
  5. malloc 申請空間失敗時,返回的是 NULL,因此使用時必須判空,new 不需要,但是 new 需要捕獲異常
  6. 申請自定義類型對象時,malloc/free 只會開闢空間,不會調用構造函數與析構函數,而 new 在申請空間後會調用構造函數完成對象的初始化,delete 在釋放空間前會調用析構函數完成空間中資源的清理
  7. new/delete 比 malloc/free 的效率稍微低點,因爲 new/delete 的底層封裝了 malloc/free

請設計一個類,該類只能在堆上創建對象

將類的構造函數私有,拷貝構造聲明成私有。防止別人調用拷貝在棧上生成對象。
提供一個靜態的成員函數,在該靜態成員函數中完成堆對象的創

class HeapOnly { 
	public: 
		static HeapOnly* CreateObject() { 
			return new HeapOnly; 
		}
	private: 
		HeapOnly() {}
	
		// C++98
		// 1.只聲明,不實現。因爲實現可能會很麻煩,而你本身不需要
		// 2.聲明成私有
		HeapOnly(const HeapOnly&)// or
		
		// C++11 
		HeapOnly(const HeapOnly&) = delete;
};

請設計一個類,該類只能在棧上創建對象

方法一:和上面類似

class StackOnly { 
	public: 
		static StackOnly CreateObject()  { 
		return StackOnly(); 
		}
	private:
		StackOnly() {}
};

方法二:只能在棧上創建對象,即不能在堆上創建,因此只要將 new 的功能屏蔽掉即可,即屏蔽掉operator new 和定位 new 表達式,注意:屏蔽了operator new,實際也將定位new屏蔽掉

class StackOnly { 
	public: 
		StackOnly() {}
	private: 
		void* operator new(size_t size);
		void operator delete(void* p);
};

內存泄漏

是什麼??
內存泄漏指因爲疏忽或錯誤造成程序未能釋放已經不再使用的內存的情況。內存泄漏並不是指內存在物理上的消失,而是應用程序分配某段內存後,因爲設計錯誤,失去了對該段內存的控制,因而造成了內存的浪費

分類
堆內存泄漏(Heap leak)
堆內存指的是程序執行中依據須要分配通過 malloc / calloc / realloc / new 等從堆中分配的一塊內存,用完後必須通過調用相應的 free 或者 delete 刪掉。假設程序的設計錯誤導致這部分內存沒有被釋放,那麼以後這部分空間將無法再被使用,就會產生 Heap Leak

系統資源泄漏
指程序使用系統分配的資源,比方套接字、文件描述符、管道等沒有使用對應的函數釋放掉,導致系統資源的浪費

如何避免內存泄漏
良好的設計規範,養成良好的編碼規範
採用 RAII 思想或者智能指針來管理資源

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