先进后出原则,最先初始化的最后析构!
1.C++中全局对象、变量的构造函数调用顺序是跟声明有一定关系的,即在同一个文件中先声明的先调用。对于不同文件中的全局对象、变量,它们的构造函数调用顺序是未定义的,取决于具体的编译器
2.C++总是按成员变量在类声明中出现的顺序来初始化成员变量的,为什么C++不按初始化列表的顺序来初始化成员变量呢?因为我们知道初始化的顺序应该与析构的顺序相反,而对一个类来说 constructor 可能有多个,初始化列表也会有多个,所以C++就选择了简单的点的方法,按成员变量出现的顺序来初始化。
3.基类的静态变量先初始化,然后是它的派生类。直到所有的静态变量都被初始化。这里需要注意全局变量和静态变量的初始化是不分次序的。这也不难理解,其实静态变量和全局变量都被放在公共内存区。可以把静态变量理解为带有“作用域”的全局变量
顺序为: 1基类的静态变量或全局变量 2派生类的静态变量或全局变量 3基类的成员变量 4派生类的成员变量
#pragama init_seg 预处理器指令
C++标准中,处于同一编译单元(cpp)的全局对象按其声明次序初始化并倒序析构,但标准中没有说处于不同编译单元的全局对象的初始化顺序。这带来了很多问题。
假如有个Log对象负责程序日志的记录。如果程序结束时,有某个全局对象出现类似于资源释放失败的错误,该对象会调用Log记录错误,这时,Log可能已经被销毁了…… 这就是所谓的dead-reference问题。
// pragma_directive_init_seg.cpp #include <stdio.h> #pragma warning(disable : 4075) //定义一个保存全局对象析构函数的指针的数组,供程序结束前显示调用来销毁全局对象: typedef void (__cdecl *PF)(void); //析构函数必须是无参数、无返回值 int cxpf = 0; // 我们需要调用的析构函数的数量 PF pfx[200]; // 保存析构函数的指针的数组. //编译器调用该函数,把pragma init_seg语句作用范围内的全局变量的析构函数全部登记入dtor函数指针数组 int myexit (PF pf) { pfx[cxpf++] = pf; return 0; } //全局对象的类型定义 struct A { A(int i) { value=i; printf("A():%d\n",i); } ~A() { printf("~A():%d\n",value); } int value; }; // 由于在pragma init_seg语句之前,这个全局变量的ctor & dtor仍然由CRT启动代码来调用, /* CRT startup code,就是在main函数(或者WinMain函数)之前和之后执行的代码, 完成运行环境初始化与事后的清理释放工作*/ A aaaa(1); //下述四行语句,模仿了编译器与链接器默认构建全局变量初始化的.CRT的开头和结尾函数指针的行为方式, //构建了用户自己的.mine节来保存全局变量初始化函数指针表的开头和结尾。 //节(Section)名字必须不多于8个字符; //在$号之前具有相同字符串的节,将被链接器合并为同一个节,合并顺序依照$号之后字符串的字典序; // InitSegStart与InitSegEnd作为边界值,使得我们可以发现那些真正的需被调用的全局变量初始化函数 #pragma section(".mine$a", read) __declspec(allocate(".mine$a")) const PF InitSegStart = (PF)1; #pragma section(".mine$z",read) __declspec(allocate(".mine$z")) const PF InitSegEnd = (PF)1; // InitializeObjects是由程序显示调用执行,来初始化pragma init_seg语句作用下的那些全局变量 // 编译时,每个节是256个字符。链接器合并这些节时,以256个字节为单位,对于空闲的字节填入0 // 所以调用函数指针前,必须与0值做比较,排除这些0值 void InitializeObjects () { const PF *x = &InitSegStart; for (++x ; x < &InitSegEnd ; ++x) if (*x) (*x)(); } //DestroyObjects由程序显示调用执行,销毁pragma init_seg语句作用下的那些全局变量 void DestroyObjects () { while (cxpf>0) { --cxpf; (pfx[cxpf])(); } } //从这条pragma init_seg语句起,所有全局变量、静态变量不再由编译器、链接器默认处理其初始化与析构, //而是由程序自身来显示执行其初始化、析构: #pragma init_seg(".mine$m", myexit) A bbbb(2); //全局变量 A cccc(3); //全局变量 int main () { InitializeObjects(); //显示初始化pragma init_seg语句作用下的那些全局变量 DestroyObjects(); //显示销毁pragma init_seg语句作用下的那些全局变量 } 程序运行结果: A():1 A():2 A():3 ~A():3 ~A():2 ~A():1
原文:http://www.cnblogs.com/yyxt/p/4912331.html