程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++ 全局構造函數調用的順序

C++ 全局構造函數調用的順序

編輯:C++入門知識

C++的全局類和靜態類的構造函數是在main函數之前調用的。但是,不同的類的構造函數以什麼順序調用呢?

 


對於g++編譯器來說,這個順序是由鏈接時,文件順序決定的。

 


我們用一個例子來說明這一點。

我們有3個文件:t1.h, t1.cpp和tt1.cpp,內容分別是

t.h


[cpp]
#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 

#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[cpp]
#include "t.h"  
 
B B::_b; 
 
A *g_a = NULL; 
 
A::A() 

    B::_b.setA(this); 
    g_a = this; 

#include "t.h"

B B::_b;

A *g_a = NULL;

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


[cpp]
#include "t.h"  
 
A a; 
 
int main() 

    printf("a=%p, b.a=%p, g_a=%p\n", &a, B::_b.a(), g_a); 

#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。

那麼,如果以這樣的順序編譯


[plain]
g++ -o t tt.cpp t.cpp 

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


[plain]
a=0x804a024, b.a=0x804a024, g_a=0x804a024 

a=0x804a024, b.a=0x804a024, g_a=0x804a024這正是我們期望的結果。

 


如果,按照這樣的順序編譯


[plain]
g++ -o t t.cpp tt.cpp 

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


[plain]
a=0x804a01c, b.a=(nil), g_a=0x804a01c 

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變量,都可以得到正確的值。

 


 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved