在C++中,new運算式用於動態建立對象,即在堆(自由儲存區)空間上為對象分配記憶體,而程式員也要小心的使用這些申請來的記憶體空間,當不再使用時應該調用delete運算式來釋放該儲存空間並且將指標置零。 本文學習了如何動態建立對象,動態建立的對象與一般對象的區別,動態建立的對象的初始化以及釋放動態分配的記憶體等知識點。
C++中分配的記憶體大致有三類:靜態儲存區,棧記憶體和堆記憶體
其中,靜態儲存區是在程式編譯階段就已經分配好的,用於全域變數,static變數等;堆棧是比較常用的Object Storage Service方式。
new和delete是C++中程式員申請和撤銷堆記憶體的唯一方式(據我所知)。
1. 建立對象
無論是定義內建類型變數還是類類型變數,都必須指定其對應的資料類型和名字。
動態建立對象只需指定其資料類型,而不必為該對象命名。
也就是說,變數定義得到的對象可以通過額外定義的指標或其對象名本身來訪問,而動態建立方式得到的對象只能通過返回的指標來訪問,因為該對象並沒有名字。
2. 初始化
a. 可以在變數定義或是動態建立的階段初始化對象,例如:
int i(222); // definition, initializedint *pi = new int(222) // dynamically, initialized
b. 在這還要說一說變數定義時的預設初始化問題:
內建類型的變數初始化與其定義位置有關:函數外的自動初始化為零,而函數內的沒有初始化。對於類類型的變數,程式總是會調用預設建構函式來初始化,這個預設建構函式可以是系統自動產生的,也可以是程式員定義的。如果沒有預設建構函式,那麼該類型變數的定義也就不能採用預設初始化方式,必須提供顯式的初始化式。
動態建立的對象預設初始化與函數內變數定義的初始化方式相同,例如:
string *ps = new string; // initialized to empty stringint *pi = new int; // pi points to an uninitialized int
c. 動態建立對象的值初始化(value-initialize)
這種值初始化不能用於變數定義,而只能用於動態建立方式。
string *ps2 = new string(); // initialized to empty stringint *pi = new int(); // pi points to an int value-initialized to 0
值初始化表明程式員想要做初始化,但並未提供特定的初值。實際上,對於提供了預設建構函式的類類型(如string),沒有必要對其對象進行值初始化:無論程式是明確地不初始化還是要求進行值初始化,都會自動調用其預設建構函式初始化該對象。值初始化真正有用的是對於內建類型。另外需要注意的一點是,值初始化的 () 文法必須置於類型名後面,而不是變數名後面,否則得到的是一個函式宣告,如下:
int myValue(); // not value-initialized int variable, but a function named myValueint *myPtr = new int(); // correct! a value-initialized int object
對於沒有預設建構函式的類類型,無論是變數定義還是動態建立對象都必須採用顯式初始化。
3. 撤銷對象
動態建立的對象用完後,程式員必須顯式地將該對象佔用的記憶體返還給自由儲存區。C++提供了delete運算式釋放指標所指向的地址空間。
delete myPtr;
上述語句釋放 myPtr 指向的 int 型對象所佔用的記憶體。
C++沒有明確定義如何釋放指向不是用 new 分配的記憶體位址的指標。此外,編譯器通常不能斷定一個指標究竟指向什麼類型的對象,因此如果調用delete 企圖釋放指向棧記憶體位址的指標時,編譯器並不會報錯,但請盡量不要依賴於該未定義的行為。
在C++中 delete 一個零值指標是合法且安全的,但實際上毫無意義。
懸垂指標(dangling pointer)
刪除了指標所指向的對象後,該指標變成懸垂指標。懸垂指標指向曾經存放對象的記憶體,但該對象實際已經不存在了。懸垂指標往往導致程式錯誤,並且很難檢測。因此,在調用delete 釋放指標所指對象記憶體後應該立刻將指標置零。
4. Const 對象的動態分配和回收
const 對象,必須在定義階段或動態建立階段進行初始化,並且初始化之後其值不能再修改。
與其他 const 對象的地址一樣,由於 new 返回的地址上存放的是 const 對象,因此該地址只能賦給指向 const 的指標。
// allocate and initialize a const objectconst int *pci = new const int(222); // initialize to 222const int *pci2 = new const int(); // initialize to 0
儘管程式員不能改變 const 對象的值,但可撤銷對象本身。
delete pci; // ok: delete a const object
三種常見的程式錯誤都與動態記憶體分配相關:
1、刪除動態分配記憶體失敗,稱為記憶體流失(memory leak)
2、讀寫已刪除的對象
3、對同一個記憶體空間使用兩次 delete 運算式。當兩個指標指向同一個動態建立的對象,刪除時就會發生錯誤。第二個指標的 delete 操作往往會破壞自由儲存區。
個人實踐部分:
#include <iostream>#include <cstring>using namespace std;int main(void){ string str = "hello str"; // strNew points to dynamically allocated, // initialized to empty string string *strNew = new string; cout<<"str object address: "<<&str<<endl; cout<<"strNew pointer itself address: "<<&strNew<<endl; cout<<"strNew pointer to address: "<<strNew<<endl; // assignment *strNew = "hello strNew"; cout<<"strNew pointer to address: "<<strNew<<endl; // free memory delete strNew; cout<<"strNew pointer to address: "<<strNew<<endl; strNew = NULL; // point to other object strNew = &str; cout<<"strNew pointer to address: "<<strNew<<endl; const int cvalue(10); // iptr points to a const int object const int *iptr = new const int(222); cout<<"iptr value: "<<*iptr<<endl; delete iptr; iptr = NULL; iptr = &cvalue; cout<<"iptr value: "<<*iptr<<endl; return 0;}
一次啟動並執行結果如下:
str object address: 0x28ff24strNew pointer itself address: 0x28ff20strNew pointer to address: 0x602f70strNew pointer to address: 0x602f70strNew pointer to address: 0x602f70strNew pointer to address: 0x28ff24iptr value: 222iptr value: 10
程式中間將原來指向 new 建立的對象的指標重新導向到一般的變數,可以看到指標存放地址的改變。另外需要注意,在釋放 new 對象之前不要將指標重新指向某個其他對象,這樣會導致原來動態建立的對象沒有指標指向它,無法釋放記憶體空間。
以上就是本文的全部內容,希望對大家的學習有所協助。