如果你是Java、C#、PHP程式員,那麼會對 new 非常熟悉,在這些程式設計語言中,隻能通過 new 來建立對象。
在C++中,你可以像定義變量一樣來建立對象,如:
<code>Student stu; //對象已被執行個體化,已配置設定記憶體空間,可以使用了 stu.say(); //調用成員函數</code>
這種情況下,系統會在棧區為對象配置設定記憶體。棧區是記憶體中的一塊區域,由系統自動配置設定和釋放,程式員無法操控,一般用來存放函數的參數值、局部變量、局部對象等。
當發生函數調用時,系統将函數參數、局部變量、局部對象依次壓入棧區;函數執行結束,再按照先進後出的原則将它們彈出(銷毀)。
對于大部分程式,這不會有什麼問題。但當你希望在函數調用結束前銷毀對象時,你是無能為力的。或者你希望通過 for 循環來建立多個對象,這種方法同樣也做不到。
這個時候 new 和 delete 就派上了用場:使用 new 建立的對象,可以在任意時刻通過 delete 銷毀,而且隻需要一個指針指向它。
以前面的 Student 類為例,可以這樣來動态建立對象:
也可以使用構造函數:
這樣,就在堆區為對象配置設定了記憶體,并調用了構造函數。
但是此時程式員還無法通路這個對象,因為這個對象既沒有名字,也沒有指針指向它。這種對象稱為
匿名對象
,它确實存在,但無法通路。
用一個指針來指向Student類的對象:
或者:
當不再需要對象時,可以通過 delete 銷毀:
這樣,就釋放掉了對象占用的記憶體,并調用了析構函數。
需要說明的是:new 在堆區為對象配置設定記憶體。與棧區不同的是,堆區記憶體由程式員配置設定和釋放,系統不會自動銷毀,即使函數調用結束了,仍然會保留堆區記憶體。如果程式員不主動回收堆區記憶體,那麼隻能在程式運作結束後由作業系統回收。
為了避免記憶體洩露,強烈建議 new 和 delete 成對出現,及時銷毀不再需要的對象。
例如,下面的代碼會造成嚴重的記憶體洩露:
<code>#include <iostream> #include <cstdlib> using namespace std; class Demo{ private: double n; double m; int i; }; void func(){ Demo *p = new Demo; } int main(){ int i; for(i=1; i<=1000000; i++){ func(); } system("pause"); return 0; }</code>
當程式運作到 system("pause"); 語句時,你可以打開任務管理器,會發現這個小小的程式竟然占用了 32M 記憶體。
這是因為每次調用 func 函數,都會建立一個對象,并用 p 指向它。函數運作結束,僅僅釋放了指針變量 p 占用的記憶體,而沒有釋放 p 所指向的對象占用的記憶體。
如果在 func 函數中不回收對象記憶體,那麼你将永遠無法回收,隻能等到程式運作結束由作業系統回收,這就是典型的記憶體洩露。
另外注意,C語言中的 malloc、free 函數不能用來為對象配置設定和釋放記憶體。請看下面的例子:
<code>#include <iostream> using namespace std; class Demo{ public: Demo(); ~Demo(); }; Demo::Demo(){ cout<<"Constructor"<<endl; } Demo::~Demo(){ cout<<"Destructor"<<endl; } int main(){ cout<<"------new------"<<endl; Demo *p1 = new Demo; //建立一個對象 Demo *p2 = new Demo[5]; //建立一組對象 cout<<"------malloc------"<<endl<<endl; Demo *p3 = (Demo*)malloc(sizeof(Demo)); cout<<"------delete------"<<endl; delete p1; //銷毀一個對象 delete[] p2; //銷毀一組對象 cout<<"------free------"<<endl; free(p3); return 0; }</code>
運作結果:
------new------
Constructor
------malloc------
------delete------
Destructor
------free------
從程式運作結果可以看出:malloc 雖然配置設定了記憶體,但沒有調用構造函數;free 雖然釋放了記憶體,但也沒有調用析構函數。