您的位置:首页 > 编程语言 > C语言/C++

C++ 全局构造函数调用的顺序

2013-07-02 23:34 337 查看
C++的全局类和静态类的构造函数是在main函数之前调用的。但是,不同的类的构造函数以什么顺序调用呢?

对于g++编译器来说,这个顺序是由链接时,文件顺序决定的。

我们用一个例子来说明这一点。

我们有3个文件:t1.h, t1.cpp和tt1.cpp,内容分别是

t.h

#ifndef T_H
#define T_H
#include <stdio.h>
class A {
public:
A();
};

class B {
public:
B(){ a_ = NULL; }
void setA(A* a) { a_ = a; }
A * a() { return a_; }

static B _b;
private:
A *a_;
};

extern A *g_a;
#endif


tt.cpp
#include "t.h"

B B::_b;

A *g_a = NULL;

A::A()
{
B::_b.setA(this);
g_a = this;
}
t.cpp

#include "t.h"

A a;

int main()
{
printf("a=%p, b.a=%p, g_a=%p\n", &a, B::_b.a(), g_a);
}
t.h定义了类A和B,其中,在A的构造中,A将自己的指针付给B::_b.a_和g_a。

那么,如果以这样的顺序编译

g++ -o t tt.cpp t.cpp
执行 ./t,得到的结果是

a=0x804a024, b.a=0x804a024, g_a=0x804a024
这正是我们期望的结果。

如果,按照这样的顺序编译

g++ -o t t.cpp tt.cpp
得到的结果是

a=0x804a01c, b.a=(nil), g_a=0x804a01c


那么,为什么先编t.cpp,在编tt.cpp,会得到b.a的结果为null呢?

这应该和ELF文件的格式有关系。

在C/C++语言中,全局变量、静态变量将被放在global数据段,当elf文件被加载到系统中时,global段的数据直接被映射到内存中。

但是,对于C++来说,全局和静态类对象,还必须调用构造函数,这些构造函数的调用,就被放在了init段。这个段是一个代码段,在elf被载入时被执行。

那么,很自然,g++按照链接的顺序,依次把全局类对象的构造放在了init段中。

于是,上面由于t.cpp先被链接,tt.cpp后被链接,因此,a的构造函数就先于B::b_的构造函数调用。 这样,当A::A()被调用时,B::b_::a_的值就被设置为a的指针。

当B::B()被调用时,a_的值被初始化为NULL。

于是,最终的输出结果,b.a=(nil)。

这说明,在C++内部,在全局构造函数中,访问其他全局或者静态变量,其结果是不可预知的。

要解决这个问题,我们使用指针变量。例如,例子中的g_a。

指针变量是一个变量而不是类对象,因此当elf文件被映射到内存中时,g_a的变量值就已经确定,无需额外的代码执行。因此,这可以保证在任意时刻访问g_a变量,都可以得到正确的值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: