#pragma init_seg使特定的全局变量优先于其他的全局变量先构造

来源:互联网 发布:陈一发 整容 知乎 编辑:程序博客网 时间:2024/05/22 18:22

原文地址:http://blog.csdn.net/yuucyf/article/details/7062543


我们知道在C++中的全局变量的构造总是优先于Main之前(应该说程序的执行体),那对于同一个文件,不同文件中定义的全局变量的构造函数是怎么执行的呢?

1)对于同一个文件来说:
全局变量的构造顺序按照全局变量的声明来构造。

如:

[cpp] view plaincopy
  1. C_C g_C;  
  2. C_A g_A;  
  3. C_B g_B;  

构造函数的执行顺序为:C_C(), C_A(), C_B(), 不管是在什么编译器上都是按照这样的顺序执行的.

2)对于不同文件来说:

全局变量的构造顺序是不确定的。

也就是说,在C.cpp文件中声明全局变量C_C g_objC, 在B.cpp文件中声明全局变量C_B g_objB, 在A.cpp文件中声明全局变量C_A g_objA, 那么这几个全局变量的构造函数的执行顺序是不确定的,也就是说它们构造函数执行的顺序是任意的,我们不能假设它们其中的一个是一定在另一个之前构造的。因为在C++标准中,处于同一编译单元(cpp)的全局对象按其声明次序初始化并倒序析构,但标准中没有说处于不同编译单元的全局对象的初始化顺序。

然而,当我们有这样的需求的时候我们怎么做到呢?如:假如有个Log对象负责程序日志的记录,如果程序结束时,有某个全局对象出现类似于资源释放失败的错误,该对象会调用Log记录错误,这时,Log可能已经被销毁了…… 这就是所谓的dead-reference问题。

这里就要引入我们在这篇文章着重介绍的部分.

3)  如何保证一个全局变量的构造函数的调用优先于所有的其他构造函数,而不管这些变量是否在同一个文件中.

利用预编译指令:#pragma init_seg({ compiler | lib | user | "section-name" [, func-name]} )

[cpp] view plaincopy
  1. <strong>pragma init_seg(compiler)    
  2. #pragma init_seg(lib)     
  3. #pragma init_seg(user)     
  4. #pragma init_seg("user_defined_segment_name")    
  5. </strong>  


init_seg 预处理器指令: 编译器、 库、 用户,和 user_defined_segment_name, 前三个指令的初始化优先次序依次降低,但都先于普通的全局变量构造,如cout就是使用compiler级别构造的。

注:一个源文件只能出现一次init_seg 指令.

pragma init_seg(compiler)是保留给微软C/C++ 运行库使用的,我们不应该使用它!
在我们自己的代码里,如果希望一些对象先于其他对象初始化,我们可以使用 #pragma init_seg(lib) 指令.
现在我们来实现一下决定全局变量的构造顺序:
(一) 对于同一个文件来说,我们把全局变量的构造函数的执行顺序强制更改.

如:

[cpp] view plaincopy
  1. //main.cpp  
  2. C_C g_C;  
  3. C_A g_A;  
  4. C_B g_B;  

对以上的代码,不管在什么环境下执行,构造函数执行顺序都是固定的,即先执行C_C(), 接着是C_A(), 最后是C_B(), 那么如果我们想更该为先执行C_A(), C_B(), 最后才执行C_C(),那我们这样更改:

[cpp] view plaincopy
  1. //A.pp  
  2. C_A::C_A()  
  3. {     
  4.           printf("C_A Constructor.\n");  
  5. }  
  6.   
  7. //B.pp  
  8. C_B::C_B()  
  9. {     
  10.           printf("C_B Constructor.\n");  
  11. }  
  12.   
  13. //C.pp  
  14. C_C::C_C()  
  15. {     
  16.           printf("C_C Constructor.\n");  
  17. }  
  18.   
  19. C_C g_C;  
  20. #pragma init_seg(lib)  
  21. C_A g_A;  
  22. C_B g_B;  
  23. int main(int argc, char* argv[])  
  24. {  
  25.     printf("Hello World!\n");  
  26.     return 0;  
  27. }  

执行结果如:

[cpp] view plaincopy
  1. C_A Constructor.  
  2. C_B Constructor.  
  3. C_C Constructor.  
  4. Hello World!  


(二)对于不同文件来说,我们把全局变量的构造函数的执行顺序强制更改.
如:以下代码文件:

[cpp] view plaincopy
  1. //A.pp  
  2. C_A g_objA;  
  3. C_A::C_A()  
  4. {     
  5.           printf("C_A Constructor.\n");  
  6. }  
  7.   
  8. //B.pp  
  9. C_B g_objB;  
  10. C_B::C_B()  
  11. {     
  12.           printf("C_B Constructor.\n");  
  13. }  
  14.   
  15. //C.pp  
  16. C_C g_objC;  
  17. C_C::C_C()  
  18. {     
  19.           printf("C_C Constructor.\n");  
  20. }  

如果由于某种需求,我要先构造C_C,接着C_B和C_A任意一个,那我们如何做到?当然我们还是要依靠Init_Seg预编译指令.
代码如下:

[cpp] view plaincopy
  1. //A.pp  
  2. C_A g_objA;  
  3. C_A::C_A()  
  4. {     
  5.           printf("C_A Constructor.\n");  
  6. }  
  7.   
  8. //B.pp  
  9. C_B g_objB;  
  10. C_B::C_B()  
  11. {     
  12.           printf("C_B Constructor.\n");  
  13. }  
  14.   
  15. //C.pp  
  16. #pragma init_seg(lib)  
  17. C_C g_objC;  
  18. C_C::C_C()  
  19. {     
  20.           printf("C_C Constructor.\n");  
  21. }  
  22.   
  23. //main.cpp  
  24. #include "A.h"  
  25. #include "B.h"  
  26. #include "C.h"  
  27.   
  28. int main(int argc, char* argv[])  
  29. {  
  30.     printf("Hello World!\n");  
  31.     return 0;  
  32. }  

执行结果如下:

[cpp] view plaincopy
  1. C_C Constructor.  
  2. C_B Constructor.  
  3. C_A Constructor.  
  4. Hello World! 
0 0