C++ 的new 運算子和C 的malloc 函數都是為了配置內存,但前者比之后者的優點
是,new 不但配置對象所需的內存空間時,同時會引發構造式的執行。
所謂構造式(constructor),就是對象誕生后第一個執行(並且是自動執行)的函數,它
的函數名稱必定要與類別名稱相同。
相對於構造式,自然就有個析構式(destructor),也就是在對象行將毀滅但未毀滅之前
一刻,最后執行(並且是自動執行)的函數,它的函數名稱必定要與類別名稱相同,再
在最前面加一個~ 符號。
#include <iostream> using namespace std; class CDemo{ char name[20]; public: CDemo(){ cout<<"class begin !!!"<<endl; } CDemo(char * ch) { strcpy(name,ch); cout << "Constructor called for " << name << '/n'; } ~CDemo(){ cout<<"desturctor called for "<<name<<endl; } }; void func() { CDemo LocalObjectInFunc("LocalObjectInFunc"); // in stack static CDemo StaticObject("StaticObject"); // local static CDemo* pHeapObjectInFunc = new CDemo("HeapObjectInFunc"); // in heap cout << "Inside func" << endl; } CDemo GlobalObject("GlobalObject"); // global static int main() { CDemo LocalObjectInMain("LocalObjectInMain"); // in stack CDemo* pHeapObjectInMain = new CDemo("HeapObjectInMain"); // in heap cout << "In main, before calling func/n"; func(); cout << "In main, after calling func/n"; system("pause"); }
以上代碼的執行結果是:
Constructor called for GlobalObject
Constructor called for LocalObjectInMain
Constructor called for HeapObjectInMain
In main, before calling func
Constructor called for LocalObjectInFunc
Constructor called for StaticObject
Constructor called for HeapObjectInFunc
Inside func
desturctor called for LocalObjectInFunc
In main, after calling func
請按任意鍵繼續. . .
所以可以得到以下幾個結論:
- 對於全域對象(如本例之GlobalObject),程序一開始,其構造式就先被執行
(比程序進入點更早);程序即將結束前其析構式被執行。MFC 程序就有這
樣一個全域對象,通常以application object 稱呼。 - 對於區域對象,當對象誕生時,其構造式被執行;當程序流程將離開該對象的
存活范圍(以至於對象將毀滅),其析構式被執行。 - 對於靜態(static)對象,當對象誕生時其構造式被執行;當程序將結束時(此
對象因而將遭致毀滅)其析構式才被執行,但比全域對象的析構式早一步執
行。 - 對於以new 方式產生出來的區域對象,當對象誕生時其構造式被執行。析構
式則在對象被delete 時執行(上例程序未示范)。
四種不同的對象生存方式(in stack、in heap、global、local static)
在C++ 中,有四種方法可以產生一個對象。
第一種方法是在堆棧(stack)之中產生它:
void MyFunc()
{
CFoo foo; // 在堆棧(stack)中產生foo 對象
...
}
第二種方法是在堆積(heap)之中產生它:
void MyFunc()
{
...
CFoo* pFoo = new CFoo(); // 在堆(heap)中產生對象
}
第三種方法是產生一個全域對象(同時也必然是個靜態對象):
CFoo foo; // 在任何函數范圍之外做此動作
第四種方法是產生一個區域靜態對象:
void MyFunc()
{
static CFoo foo; // 在函數范圍(scope)之內的一個靜態對象
...
}
不論任何一種作法,C++ 都會產生一個針對CFoo 構造式的調用動作。前兩種情況,C++
在配置內存-- 來自堆棧(stack)或堆積(heap)-- 之后立刻產生一個隱藏的(你的原
代碼中看不出來的)構造式調用。第三種情況,由於對象實現於任何「函數活動范圍
(function scope)」之外,顯然沒有地方來安置這樣一個構造式調用動作。
是的,第三種情況(靜態全域對象)的構造式調用動作必須靠startup 碼幫忙。startup 碼
是什么?是更早於程序進入點(main 或WinMain)執行起來的碼,由C++ 編譯器提供,
被聯結到你的程序中。startup 碼可能做些像函數庫初始化、進程信息設立、I/O stream 產
生等等動作,以及對static 對象的初始化動作(也就是調用其構造式)。
當編譯器編譯你的程序,發現一個靜態對象,它會把這個對象加到一個串行之中。更精
確地說則是,編譯器不只是加上此靜態對象,它還加上一個指針,指向對象之構造式及
其參數(如果有的話)。把控制權交給程序進入點(main 或WinMain)之前,startup 碼
會快速在該串行上移動,調用所有登記有案的構造式並使用登記有案的參數,於是就初始化了你的靜態對象。
第四種情況(區域靜態對象)相當類似C 語言中的靜態區域變量,只會有一個實體
(instance)產生,而且在固定的內存上(既不是stack 也不是heap)。它的構造式在
控制權第一次移轉到其聲明處(也就是在MyFunc 第一次被調用)時被調用。