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 */ }