我们可以通过代码1所示的示例程序观察到C++中一个关于全局类变量初始化顺序的有趣的现象。

    class1.cpp                                                                        
    #include <iostream>                                                 
                                                                                  
    class class1_t                                                         
    {                                                                            
    public:
        class1_t ()
        {
            std::cout << "class1_t::class1_t ()" << std::endl;
        }
    };
    static class1_t s_class1;
    main.cpp
    #include <iostream>
    class class2_t
    {
    public:
        class2_t ()
        {
            std::cout << "class2_t::class2_t ()" << std::endl;
        }
    };
    static class2_t s_class2;
    int main ()
    {
        return 0;
    }

代码1

  示例程序分别在两个文件中定义了一个类和该类的一个静态全局变量,各类在其构造函数中输出其名。为了简单我们让main()函数的实现是空的。我们知道,全局类变量会在进入main()函数之前被构造好,且是在退出main()函数后才被析构。

  代码2示例了不同编译方法所获得可执行程序的运行结果。两种编译方法的区别是交换main.cpp和class1.cpp在编译命令中的顺序。从结果来看,示例程序内两个全局变量的构造顺序与文件编译时的位置有关。

    $ g++ main.cpp class1.cpp -o example
    $ ./example.exe
    class1_t::class1_t ()
    class2_t::class2_t ()
    $ g++ class1.cpp main.cpp -o example
    $ ./example.exe
    class2_t::class2_t ()
    class1_t::class1_t ()

代码2

  为什么会出现这样的有趣现象呢?我们需要了解编译器是如何处理全局类变量的,这需要查看编译器的源码和使用binutils工具集。

  可以肯定的是,编译时的文件顺序会影响ld链接器对目标文件的处理顺序。让我们先了解ld链接器的默认链接脚本。通过代码3的命令可以获得ld自带的链接脚本,代码4例出了这里需要关心的脚本片断。

$ ld --verbose > ldscript

代码3