class X{ public: void *ptr; X(){ ptr=(void*)1;RepStatF("Construct X"); } ~X(){ ptr=0;RepStatF("Destruct X"); } }; void f(void *ptr){ RepStatF("In f: %lu",ptr); } int main(int argc,char *argv[]){ f(X().ptr); /** * 查看彙編文件: * call _ZN1XC1Ev //構造一個X對象 * call _Z1fPv * call _ZN1XD1Ev //釋放X對象 * 因此此時就至關於: * X _noname; * f(_noname.ptr); * _noname.~X() **/ return 0; }
生命期從程序開始到程序結束,也即在整個程序執行期間都存在.多線程
底層實現:g++是把全局變量/靜態變量(包括在{}內定義的靜態變量)放在程序文件的數據段,內核執行程序文件的惟一方法就是經過exec()加載.而exec()會從程序文件中加載數據段與正文段,也即全局變量/靜態變量在程序開始執行以前就已經存在了.函數
void f(){ static int fffifff=33; }/* 函數內的靜態變量 */ int iii=77; /* 全局變量 */ --------------------------------------------------- .data /* 數據段 */ .align 4 .type iii, @object .size iii, 4 iii: /* 全局變量 */ .long 77 .align 4 .type _ZZ1fvE7fffifff, @object .size _ZZ1fvE7fffifff, 4 _ZZ1fvE7fffifff: /* 靜態變量,進行了名稱修改 */ .long 33
定義在全局做用域的對象是放在程序的bss段,模式:
.bss
全局對象名 .zero 全局對象所佔字節(即sizeof(全局對象))this
_Z41__static_initialization_and_destruction_0ii這個函數中完成對全局類對象構造函數的調用,以及析夠函數的註冊.spa
由於只有exit()函數會依次調用清理處理程序,因此只有當直接調用exit()退出時或者從main函數返回(此時返回到特殊啓動例程,該例程會調用exit())纔會調用全局類對象的析夠函數.線程
將析夠函數經過__cxa_atexit()註冊爲程序清理處理程序,即在進程退出前調用.翻譯
class X{ char _buf[16]; public: X(const char *buf){ strcpy(_buf,buf);Println("構造: %s",_buf); } ~X(){ Println("析夠: %s",_buf); } }; X x1("HelloWorld"); int main(int argc,char *argv[]){ _exit(0); /* 此時不會調用x1的析夠函數,不過也不須要,由於進程已經終止了 */ exit(0); /* 或者 return 0,此時會調用析鉤函數 */ } ---------------------------------------- .globl x1 .bss /* bss段進行變量的定義 */ .align 16 .type x1, @object .size x1, 16 x1: .zero 16 /* 16個字節 */ _Z41__static_initialization_and_destruction_0ii:/* 構造函數調用與析夠函數註冊 */ movl $.LC2, %esi /* buf參數 */ movl $x1, %edi /* this指針 */ call _ZN1XC1EPKc /* 調用構造函數 */ movl $__dso_handle, %edx movl $x1, %esi /* this指針 */ movl $_ZN1XD1Ev, %edi/* 析夠函數地址 */ call __cxa_atexit /* 註冊析鉤函數 */
存放在程序文件的bss段,模式:.comm 修飾後的對象名 對象佔用字節指針
構造函數是在函數(更具體:在對象所處的做用域)中調用,析鉤函數也是在函數中進行註冊,而且只會調用/註冊一次.如code
if(函數第一次執行){/* 確保了在多線程中靜態對象也只會被初始化一次. */ 調用構造函數 註冊析鉤函數 }
class X{ public: X() { RepStatF("構造函數"); } ~X() { RepStatF("析鉤函數"); } }; void* test(void *){ static X x; /* 只會被初始化一次 */ RepStatF("Creating..."); RepStatF("Exiting..."); return 0; } int main(int argc,char ** argv){ RepStatF("main Thread..."); CT.create(test);/* 線程1 */ CT.create(test); /* 此時'線程1'在執行test()函數中已經完成了對靜態局部變量x * 的初始化,因此該線程不會初始化x. */ CT.sleepus(3000000); } /* -----------------運行輸出---------------------------------------------- */ PID 線程ID 時間 說明 4628: 0x7f921f88d740: 1394381143949726: main Thread... 4628: 0x7f921e882700: 1394381143966725: 構造函數/* 在最初建立的線程中進行初始化 */ 4628: 0x7f921e882700: 1394381143966771: Creating... 4628: 0x7f921e882700: 1394381143966788: Exiting... 4628: 0x7f921e081700: 1394381143966779: Creating... 4628: 0x7f921e081700: 1394381143966867: Exiting... 4628: 0x7f921f88d740: 1394381146959016: 析鉤函數/* 在主線程進行初始化 */
由上知,在函數內部定義靜態變量,變量只會被初始化一次,因此如下會有意料的結果:對象
void f(int i){ static int x=i>7?3:7; Println("HelloWorld: %d",x); /** * f()翻譯成彙編應該是: * if(函數第一次執行){ * if(i>7) mov 3,x; * else mov 7,x; * } * mov "HelloWorld",%rdi * call printf **/ } int main(int argc,char *argv[]){ f(9); /* 輸出'HelloWorld: 3' */ f(2); /* 輸出'HelloWorld: 3' */ } ------------------------------------------------------------- class X{ char _buf[16]; public: X(const char *buf){ strcpy(_buf,buf);Println("構造: %s",_buf); } ~X(){ Println("析夠: %s",_buf); } }; void f(const char *buf){ static X x1(buf); } int main(int argc,char *argv[]){ f("Hello"); /* 輸出Hello */ f("World"); /* 輸出Hello */ }