1. 程式人生 > >C++學習之記憶體的分配和初始化

C++學習之記憶體的分配和初始化

C++定義了2個運算子來分配和釋放動態記憶體。new分配記憶體,delete釋放記憶體。

1. 使用new動態分配和初始化物件

在自由空間分配的記憶體是無名的,new返回一個指向分配的物件的指標。

	int *pi = new int; // pi指向一個動態分配的、未初始化的無名物件

預設情況下,動態分配的物件是預設初始化,內建型別(int, double)或組合型別(struct)的物件的值是未定義的,十分危險。類型別(vector、string)物件使用預設建構函式進行初始化。

	string *ps = new string; // string初始化為空
	int *pi = new int; // pi指向一個未初始化的int

使用圓括號對物件進行初始化

	int *q1 = new int(1024);  // q1指向的物件的值為1024
	string *ps = new string(10, '9'); // 初始化為"9999999999"

使用列表初始化(花括號)

	vector<int> *pv = new vector<int>{0,1,2,3,4,5,6,7,8,9};

對動態分配的物件進行值初始化,只需在型別名之後跟一對空括號即可

	string *ps1 = new string; // 預設初始化為空string
	string *ps2 = new string();  // 值初始化為空string
	int *pi1 = new int;   // 預設初始化,*pi1的值未定義
	int *pi2 = new int(); // 值初始化為0, *pi2為0

對於定義了自己的建構函式的類型別來說,物件都會通過預設建構函式進行初始化。而內建型別則不具備這個功能,需要我們對其初始化。
若我們提供了一個括號包圍的初始化器,則可以使用auto根據初始化器來自動推斷我們想分配的物件的型別。由於編譯器要用初始化器的型別來推斷要分配的型別,只有當括號中僅有單一初始化器時才可以使用auto:

auto p1 = new auto(pi1);    //  與pi1型別相同的物件,該物件用pi1進行初始化,p1是int *
auto p2 = new auto(pi1,pi2);  //  ERROR:括號中只能由單個初始化器

動態分配的const物件必須初始化

用new分配const物件。

	const int *pci = new const int(1024);   // 分配並初始化一個const int
	const string *pcs  = new const string;

若系統記憶體耗盡,則記憶體分配失敗,new會丟擲bad_alloc的異常。可以使用定位new來阻止拋異常:

	int *p3 = new (nothrow) int; // 若分配失敗,new返回一個空指標

2、釋放動態記憶體

動態記憶體使用完畢後,使用delete將記憶體歸還給系統。delete執行2個動作:銷燬給定的指標指向的物件;釋放對應的記憶體。傳遞給delete的指標必須指向動態分配的記憶體或一個空指標。

	delete p3;

const物件的值不能被改變,但是其本身可以被銷燬。

	const int *pci = new const int(1024);  // 分配並初始化一個const int
	delete pci;  // 釋放一個const物件

動態物件的生存期

對於一個由內建指標管理的動態物件,直到被顯式釋放之前都是存在的。
動態記憶體的使用存在的三個常見問題:

  1. 忘記delete記憶體
  2. 使用已經釋放的物件
  3. 同一記憶體多次釋放

delete一個指標之後,指標值變得無效,但是很多機器上依然儲存著動態記憶體的地址,指標變成了空懸指標:指向一塊曾經儲存資料但是現在已經無效的記憶體的指標。
避免空懸指標:在指標即將離開其作用域之前釋放掉關聯的記憶體,若需要保留指標,可以在delete之後將nullptr賦予指標,表明指標不指向任何物件。

使用智慧指標可以避免這些問題。

3、shared_ptr和new的結合

若不初始化一個智慧指標,則它會初始化為一個空指標,可以用new返回的指標來初始化智慧指標。

	shared_ptr<double> p4;  // shared_ptr指向一個double

接收指標引數的智慧指標建構函式是explicit的,不能將一個內建指標轉換為智慧指標,必須採用直接初始化:

	shared_ptr<int> p5 = new int(42); // ERROR
	shared_ptr<int> p5(new int(42)); // p2指向一個值為42的int

一個返回shared_ptr的函式不能在其返回語句中隱式轉換一個普通指標:

shared_ptr<int> clone(int p){
	return new int(p);   // error: 隱式轉換為shared_ptr<int>
}

shared_ptr<int> clone(int p){
	return shared_ptr<int>(new int(p)); // 正確:顯式用int*建立shared_ptr<int>
}

一個用來初始化智慧指標的普通指標必須指向動態記憶體,因為智慧指標預設使用delete釋放它關聯的物件,可以將智慧指標繫結到一個指向其他型別的資源的指標上。
在這裡插入圖片描述
在這裡插入圖片描述