C++ 全局构造函数调用的顺序
2013-07-02 23:34
337 查看
C++的全局类和静态类的构造函数是在main函数之前调用的。但是,不同的类的构造函数以什么顺序调用呢?
对于g++编译器来说,这个顺序是由链接时,文件顺序决定的。
我们用一个例子来说明这一点。
我们有3个文件:t1.h, t1.cpp和tt1.cpp,内容分别是
t.h
tt.cpp
那么,如果以这样的顺序编译
如果,按照这样的顺序编译
那么,为什么先编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变量,都可以得到正确的值。
对于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变量,都可以得到正确的值。
相关文章推荐
- C++ 全局构造函数调用的顺序
- c++ 子类父类 的构造函数调用顺序
- c++中 子类父类的构造函数调用顺序
- 【C++】基类和派生类构造函数的调用顺序
- C++ 构造函数和析构函数的调用顺序、虚析构函数的作用
- C++学习笔记(5)——基类、派生类的构造函数、析构函数的调用顺序
- C++多重继承中构造函数和析构函数调用顺序举例
- C++构造函数调用顺序
- C++中多重继承构造函数调用的先后顺序
- C++调用构造函数与析构函数的顺序
- C++ 构造函数和析构函数的调用顺序、虚析构函数的作用
- C++中对象作为函数形参,返回值时,构造函数,复制构造函数,析构函数的调用顺序(1)
- C++学习笔记(调用构造函数和析构函数的顺序)
- C++继承中构造函数、析构函数调用顺序及虚函数的动态绑定
- C++之派生类的构造函数和析构函数调用顺序
- C++继承中析构函数 构造函数的调用顺序以及虚析构函数
- C++多重继承,菱形继承中构造函数的调用顺序
- c++学习笔记4,派生类的构造函数与析构函数的调用顺序(一)
- C++继承中构造函数、析构函数调用顺序及虚函数的动态绑定
- [C++] 复杂继承关系中的构造函数调用顺序