C++的內存管理詳細解釋

一、C/C++內存分佈

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

在這裡插入圖片描述

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

1、malloc/calloc/realloc區別:

共同點:

  • 都是C語言中用來進行動態內存申請的庫函數,申請的空間都在堆上,用完之後必須使用free來進行釋放
  • 返回值類型都是void*在接受返回的地址時必須要進行強轉
  • 如果申請空間成功:返回的是空間的首地址,如果失敗返回的是NULL

不同點:

  • malloc:返回值類型void*

在接受返回的空間地址時必須要進行強轉

成功:空間首地址 失敗:NULL

參數:申請的空間所占總的字節數

申請的空間在堆上,使用完成後必須要使用free來進行釋放

  • calloc:返回值一致

參數列表:參數一表示元素的個數;參數二表示單個類型的字節數

功能:與malloc基本相同,但是calloc會對其申請的空間進行初始化

  • realloc(void* p, size_t size):將p所指向空間的大小調整到size字節

p指向的是NULL:該函數的類似malloc

假設:p所指向的空間總共占old個字節

size <= old:將p所指向的空間縮小到size個字節—->直接返回值p所指向空間的首地址

size > old:將p所指向的空間擴增到size個字節

大一點:返回原空間的首地址

大的多的多:申請新空間;將舊空間中元素拷貝到新空間;釋放舊空間;返回新空間的首地址

三、C++中動態內存管理:new/delete

C++為什麼要搞一套動態內存管理?

首先:C語言中的動態內存管理方式在C++中仍然可以使用

原因:

1、C語言中的方式比較麻煩—需要用戶手動算字節數,需要對返回結果強轉,需要判空,需要包含頭文件

2、malloc、free:不會調用構造函數/析構函數;new、delete:在進行空間申請和釋放時是會調用構造函數和析構函數的

在這裡插入圖片描述

// C++中動態內存管理方式:new/delete----申請單個類型的空間
// new[]/delete[]-----申請釋放一段連續的空間
//註意:1、new/delete不是函數,是C++中的關鍵字||操作符
//		2、new的空間必須要有delete釋放  new[]必須使用delete[]釋放
class Test
{
public:
	Test()
		:_t(10)
	{
		cout << "Test():" << this << endl;
	}
	~Test()
	{
		cout << "~Test():" << this << endl;
	}
	int _t;
};
//new/delete和new[]/delete[]使用說明
void Test1()
{
	int* p1 = new int;
	int* p2 = new int(100);
	int* p3 = new int[10];
	int* p4 = new int[10]{ 1,2,3,4,5,6,7,8,9,0 };
	delete p1;
	delete p2;
	delete[] p3;
	delete[] p4;
}
void Test2()
{
	//malloc並不是創建瞭一個Test類型的對象,隻是在堆上申請瞭一塊與Test類型大小相同的一塊空間
	//因為:malloc不調用構造函數
	Test* p1 = (Test*)malloc(sizeof(Test));//malloc不會調用構造函數
	if (nullptr == p1)
		return;
	//真正創建瞭一個對象,該對象的空間在堆上
	Test* p2 = new Test;//new在申請空間期間會調用構造函數
	free(p1);//:在釋放空間期間,不會調用析構函數
	delete p2;//:在釋放空間期間,會調用對象的析構函數
}
/*
在C++中,如果想要在堆上申請空間:
1、采用C語言中的malloc、calloc、realloc,但是並不能申請對象的空間
2、采用new/new[]---可以調用構造函數,註意:如果使用new[]申請連續的空間是,該類必須提供無參或全缺省的構造函數
3、malloc/free,new/delete,new[]/delete[]必須成對使用,否則會內存泄漏或者代碼崩潰
*/

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

四、實現原理

new的原理

1、調用operator new函數申請空間

2、在申請的空間上執行構造函數,完成對象的構造

delete的原理

1、在空間上執行析構函數,完成對象中資源的清理工作

2、 調用operator delete函數釋放對象的空間

new T[N]的原理

1、調用operator new[]函數,在operator new[]中實際調用operator new函數完成N個對象空間的申

2、在申請的空間上執行N次構造函數

delete[]的原理

1、 在釋放的對象空間上執行N次析構函數,完成N個對象中資源的清理

2、調用operator delete[]釋放空間,實際在operator delete[]中調用operator delete來釋放空間

五、面試常問問題

1、malloc/free和new/delete的區別

共同點是

都是從堆上申請空間,並且需要用戶手動釋放。

不同的地方是:

  • malloc和free是函數,new和delete是操作符
  • malloc申請的空間不會初始化,new可以初始化
  • malloc申請空間時,需要手動計算空間大小並傳遞,new隻需在其後跟上空間的類型即可
  • malloc的返回值為void*, 在使用時必須強轉,new不需要,因為new後跟的是空間的類型
  • malloc申請空間失敗時,返回的是NULL,因此使用時必須判空,new不需要,但是new需要捕獲異常
  • 申請自定義類型對象時,malloc/free隻會開辟空間,不會調用構造函數與析構函數,而new在申請空間後會調用構造函數完成對象的初始化,delete在釋放空間前會調用析構函數完成空間中資源的清理

2、內存泄漏

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

內存泄漏的危害:長期運行的程序出現內存泄漏,影響很大,如操作系統、後臺服務等等,出現內存泄漏會導致響應越來越慢,最終卡死

內存泄漏分類(瞭解)

C/C++程序中一般我們關心兩種方面的內存泄漏:

1、堆內存泄漏(Heap leak)

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

2、系統資源泄漏

指程序使用系統分配的資源,比方套接字、文件描述符、管道等沒有使用對應的函數釋放掉,導致系統資源的浪費,嚴重可導致系統效能減少,系統執行不穩定。

如何避免內存泄漏

1、工程前期良好的設計規范,養成良好的編碼規范,申請的內存空間記著匹配的去釋放。ps:這個理想狀態。但是如果碰上異常時,就算註意釋放瞭,還是可能會出問題。需要下一條智能指針來管理才有保證。

2、 采用RAII思想或者智能指針來管理資源。

3、有些公司內部規范使用內部實現的私有內存管理庫。這套庫自帶內存泄漏檢測的功能選項。

4、出問題瞭使用內存泄漏工具檢測。ps:不過很多工具都不夠靠譜,或者收費昂貴。

解決方案:

1、事前預防型。如智能指針等。

2、事後查錯型。如泄漏檢測工具。

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: