一反應main()函數是所有函數執行的開始。但是問題是main()函數執行之前如何執行呢?
聯想到MFC裡面的 C**App類的theApp對象,其執行順序就在main函數之前。道理相通,順理推下,能夠想到:如果在main函數之前聲明一個類的全局的對象。那麼其執行順序,根據全局對象的生存期和作用域,肯定先于main函數。
示例如下:
class simpleClass
{
public:
simpleClass( )
{
cout << "simpleClass constructor.." << endl; //step2
}
};
simpleClass g_objectSimple; //step1全局對象
int _tmain(int argc, _TCHAR* argv[]) //step3
{
return 0;
}
可單步調試檢視執行順序為step1、step2、step3。
考慮到全局對象,同理會進一步思考靜态對象的作用域。将上述示例進一步擴充如下:
class simpleClass
{
public:
simpleClass( )
{
cout << "simpleClass constructor.." << endl; //step2
}
};
class simpleClassTwo
{
public:
static simpleClass m_sSimpleClass;
};
simpleClass simpleClassTwo::m_sSimpleClass = simpleClass(); //step1 靜态對象
int _tmain(int argc, _TCHAR* argv[]) //step3
{
return 0;
}
可單步調試檢視執行順序為step1、step2、step3。
至此,我們可以總結出:*定義在main( )函數之前的全局對象、靜态對象的構造函數在main( )函數之前執行。
再進一步思考,既然可以在main( )函數之前執行全局、靜态對象的構造函數。那麼有沒有函數在main( )函數之後執行呢?
有的,onexit函數。原型如下:
_onexit_t _onexit(
_onexit_t function
);
_onexit_t_m _onexit_m(
_onexit_t_m function
);
解釋:
The _onexit function is passed the address of a function (function) to be called when the program terminates normally. Successive calls to _onexit create a register of functions that are executed in LIFO (last-in-first-out) order. The functions passed to _onexit cannot take parameters.
核心點:
1)執行期——程式執行終止的時候;
2)傳遞參數——函數的位址,即函數指針;
3)執行順序——後進先出。
_onexit is a Microsoft extension. For ANSI portability, use atexit. The _onexit_m version of the function is for mixed mode use.
onexit是微軟的擴充版本,标準C++裡面應用的是atexit。
【MSDN】示例:
#include <stdlib.h>
#include <stdio.h>
/* Prototypes */
int fn1(void), fn2(void), fn3(void), fn4 (void);
int main( void )
{
_onexit( fn1 );
_onexit( fn2 );
_onexit( fn3 );
_onexit( fn4 );
printf( "This is executed first.\n" );
}
int fn1()
{
printf( "next.\n" );
return 0;
}
int fn2()
{
printf( "executed " );
return 0;
}
int fn3()
{
printf( "is " );
return 0;
}
int fn4()
{
printf( "This " );
return 0;
}
執行結果如下:
顯然,讀程式可以看出main( )函數執行完畢後又執行了onexit( )函數。
還有沒有其他特殊的情況呢?持續探讨、更新中……
補充:控制台界面應用程式是如何啟動的?
以Windows平台為例,用MicrosoftVisual Studio 來建立一個應用程式項目時,內建開發環境會設定各種連接配接開關,使連結器将子系統的正确類型嵌入最終生成的可執行檔案(executable)中。對于控制台界面應用程式,這個連結器的開關是/SUBSYSTEM:CONSOLE。
使用者啟動應用程式時,作業系統的加載程式(loader)會檢查可執行檔案映像的檔案頭,并擷取這個子系統值。如果如下圖所示,子系統值為/SUBSYSTEM:CONSOLE,加載程式會自動確定指令符啟動程式的時候有一個可用的文本控制台視窗。另外,如有必要,如從資料總管啟動CUI程式的時候,會建立一個新視窗。
在連接配接可執行檔案時,連結器将選擇正确的C/C++運作庫啟動函數。如果指定了/SUBSYSTEM:CONSOLE,會執行如下步驟:
所有C/C++運作庫啟動函數所做的事情基本都是一樣的,差別1在于它們處理字元串的類型(ANSI字元串或者是Unicode字元串),差別2在于它們調用的是哪個入口點函數。
Visual C++自帶C++運作庫的源代碼啟動函數的用途簡單概括如下:
1) 擷取新程序完整指令行的一個指針;
2) 擷取指向新程序的環境變量的一個指針;
3) 初始化C/C++運作庫的全局變量。
4) 初始化C運作庫記憶體配置設定函數(malloc和calloc)和其他底層的I/O例程使用的堆。
5) 調用所有全局和靜态C++類對象的構造函數。
第5條也就解釋了為什麼在main()函數前運作全局、靜态變量的構造函數了。
入口點函數傳回後,啟動函數将調用C運作庫函數exit,向其傳遞傳回值(nMainRetVal)。exit函數執行以下任務:
1) 調用_onexit函數所有調用所注冊的任何一個函數;
2) 調用所有全局和靜态C++類對象的析構函數;
3) 在DEBUG生成中,如果設定了_CRTDBG_LEAK_CHECK_DF标志,就通過調用_CrtDumpMemoryLeaks函數生成記憶體洩露報告。
4) 調用作業系統的ExitProcess函數,向其傳入nMainRetVal。這會導緻作業系統殺死我們的程序,并設定它的退出代碼。
exit( )函數的執行的先後順序為:1)、2)、3)、4)。
如下上述程式的合體,驗證了exit函數的執行順序。
先全局對象構造函數,然後執行main函數列印語句,再執行_onexit注冊函數;最後執行全局對象析構函數。
#include <stdlib.h>
#include <stdio.h>
class simpleClass
{
public:
simpleClass()
{
cout<< "simpleClass constructor.." << endl;
}
~simpleClass()
{
cout<< "~SimpleClass Destructor.." << endl;
}
};
simpleClass g_objectSimple; //1全局對象
/* Prototypes */
int fn1(void), fn2(void), fn3(void), fn4(void);
int main( void )
{
_onexit(fn1 );
_onexit(fn2 );
_onexit(fn3 );
_onexit(fn4 );
printf("This is executed first.\n" );
}
int fn1()
{
printf("next.\n" );
return0;
}
int fn2()
{
printf("executed " );
return0;
}
int fn3()
{
printf("is " );
return0;
}
int fn4()
{
printf("This " );
return0;
}

作者:銘毅天下
轉載請标明出處,原文位址:
http://blog.csdn.net/laoyang360/article/details/8820501