C++動態記憶體管理——new/delete

audience_fzn發表於2018-08-06

1.c語言的動態記憶體管理

  • malloc:動態開闢指定大小的空間,返回值是void* ,所以要自己指定返回的資料型別
    • int *ptr = (int*) malloc (10*sizeof(int));
  • calloc:動態開闢指定大小的空間,與malloc不同的是它會進行初始化
    • int* ptr = (int*)calloc(10,sizeof(int));
  • realloc: 為動態開闢的空間調整大小,如果原空間後有足夠大的空間,之間在後面開闢。如果原空間後的空間不夠大,它會開闢一塊空間,將原來的資料拷貝過來,並釋放原來的空間

int *ptr = (int*) malloc (10*sizeof(int));

realloc(ptr,100*sizeof(int));

  • free:動態開闢的空間一定要記得釋放,否則可能導致記憶體洩漏(指標丟了,記憶體還在)

動態開闢的記憶體都在堆上,堆上的空間由程式設計師手動開闢,手動釋放。

 

2.c++的動態記憶體管理:

c++通過new/delete動態管理記憶體

  • new/delete動態管理物件
  • new[]/delete[]動態管理物件陣列
  • new做了倆件事:
  1. 呼叫operator new分配空間
  2. 呼叫了建構函式初始化物件
  • delete做了倆件事
  1. 呼叫解構函式權力物件
  2. 呼叫operator delete釋放空間
  • new[N]
  1. 呼叫operator new分配空間
  2. 呼叫N次建構函式分別初始化每個物件
  • delete[N]
  1. 呼叫N次解構函式清理物件
  2. 呼叫operator delete釋放空間
void Test()
{
	int *p1 = new int;//動態分配4位元組(1個int)的空間單個資料
	int *p2 = new int(3);//動態分配4位元組(1個int)的空間並初始化
	int *p3 = new int[3];//動態開闢12個位元組(3個int)的空間

	delete p1;
	delete p2;
	delete[] p3;
}

注意:malloc和free,new/delete,new[]/delete[]要配對使用,否則可能出現記憶體洩漏甚至崩潰的問題

問:為什麼c++不能使用mallco/free來動態管理記憶體呢?

答:c語言是程式導向的語言,如果函式出錯返回錯誤碼(malloc/free出錯返回錯誤碼)

c++面對物件程式設計,出錯會拋異常

 

3.c++的其他記憶體管理介面

  • void* operator new(size_t size);
  • void operator delete(size_t size);
  • void *operator new[](size_t size);
  • void operator delete[](size_t size);

標準庫對函式operator new和operator delete的命名容易讓人誤解,與其他operator函式(如operator=)不同,他這些函式並不是對new和delete的過載,只是malloc和free的 一層封裝,實際上我們不能對new和delete過載

  1. operator new/operator delete;operator new[]/operator delete[]和malloc/free用法一樣
  2. 他們只負責分配空間,不會呼叫物件建構函式/解構函式來初始化/清理物件
  3. 實際上operator new和operator delete只是malloc和free的 一層封裝

 

4.maollc/free與new/delete的區別,聯絡

  • 他們都是動態管理記憶體的入口
  • malloc/free是函式,new/delete是操作符
  • malloc要手動計算開闢空間的帶下,new開闢空間的大小由系統計算
  • malloc/free不呼叫構造/解構函式,new/delete呼叫建構函式/解構函式進行初始化和函式清理
  • malloc失敗返回錯誤碼,new失敗拋異常

5.malloc/free,new/delete,new[]/delete[]不匹配使用會出現哪些問題?

1)內建型別

  • 不匹配使用也不會出現程式出錯、崩潰等問題
  • 他們不需要呼叫構造/解構函式,不用儲存count,所以不會出現訪問記憶體出錯的問題

2)自定義型別:

  • 不呼叫建構函式,不會崩潰
  • 呼叫建構函式,會崩潰,因為要儲存count ,容易出現記憶體訪問出錯
  • 編譯器會自動優化,如果自定義型別中沒有定義構造/析構,或者構造/解構函式內部什麼都沒有做,就不會多開闢用來儲存count的4個位元組

以下為內建型別的驗證(不匹配使用不會出錯,但是我們仍建議配對使用):


void Test()
{
	int *p1 = new int;
	int *p2 = new int(3);
	int *p3 = new int[3];
	int *p4 = (int*)malloc(sizeof(int));

	delete[] p1;
	//free(p1);
	delete p2;
	//free(p2);
	//delete p3;
	free(p3);
	delete p4;
}

自定義型別:

class AA
{
public:
	AA(size_t size = 10)
		:_size(size)
		, _a(0)
	{
		cout << "AA()" << endl;
	}
	~AA()
	{
		cout << "~AA()" << endl;
		if (_a)
		{
			delete[] _a;
			_a = 0;
			_size = 0;
		}
	}
private:
	int* _a;
	size_t _size;
};

 情況一:new

void Test()
{
	AA* p1 = new AA;

	delete p1;
        //正確
	//delete[] p1;
        //錯誤,因為delete[]對應new[],new[]在開闢空間的時候多加了4,delete[]就會-4向前面四個位元組去取,所以會出錯
	free(p1);
        //正確,但是沒有呼叫解構函式
}

情景二: new(3),和情景一情況一樣,只是多了初始化

void Test()
{
	AA* p2 = new AA(3);

	delete p2;
        //正確
	//delete[] p2;
        //程式出錯,因為delete[]對應new[],new[]在開闢空間的時候多加了4,delete[]就會-4向前面四個位元組去取,所以會出錯
	//free(p2);
        //正確,但是沒有呼叫解構函式
}

情景三:

void Test()
{
	AA* p1 = new AA;
	AA* p2 = new AA(3);
	AA* p3 = new AA[3];

	delete p1;
	delete p2;
	delete[] p3;
        //正確
	//delete p3;
//錯誤,delete直接跳過開始4位元組從後面開始釋放,所以前面的四個位元組並沒有釋放,delete[]從new[]多建立的4個位元組處開始釋放
	//free(p3);
        //程式崩潰
}

 場景四:

void Test()
{

	AA* p4 = (AA*)malloc(sizeof(AA));

	free(p4);//正確
	delete p4;//解構函式什麼都不做時正確
	delete[] p4;//錯誤
}

 

 

相關文章