3

我正在考慮一個特定的解決方案,我想初始化一個在其他模塊中定義的數組的單元格(將有許多模塊初始化一個表)。在運行main之前,數組不會被讀取(所以沒有靜態初始化順序的問題)。部分初始化在其他模塊中定義的變量

我的方法:

/* secondary module */ 

extern int i[10]; // the array 

const struct Initialize { 
    Initialize() { i[0] = 12345; } 
} init; 


/* main module */ 

#include <stdio.h> 


int i[10]; 

int main() 
{ 
    printf("%d\n", i[0]); // check if the value is initialized 
} 

編譯器不會去掉init恆定的,因爲構造函數有副作用。我對嗎?機制好嗎?在GCC(-O3)上一切都很好。

//編輯
在現實世界中會有很多模塊。 我想避免一個額外的模塊,一箇中央的地方,將收集所有小的初始化例程(爲了更好的可伸縮性)。所以這很重要,每個模塊都會觸發自己的初始化。

+0

您仍然認爲在每個模塊初始化期間,i []數組已經分配(未初始化)。 – 2010-07-06 16:20:14

+0

當應用程序啓動時,AFAIK靜態存儲空間在一個步驟中被分配(並歸零)。 – adf88 2010-07-06 16:25:45

+0

UP:它是**全局**靜態存儲空間 – adf88 2010-07-06 16:32:27

回答

3

這適用於MSVC編譯器,但與GNU C++不(至少對我而言)。 GNU鏈接器將剝離編譯單元外部未使用的所有符號。我知道只有一種方法來保證這種初始化 - 「init init」成語。對於examle:

init_once.h:

template <typename T> 
class InitOnce 
{ 
    T *instance; 
    static unsigned refs; 
public: 
    InitOnce() { 
     if (!refs++) { 
      instance = new T(); 
     } 
    } 

    ~InitOnce() { 
     if (!--refs) { 
      delete instance; 
     } 
    } 
}; 
template <typename T> unsigned InitOnce<T>::refs(0); 

unit.h:

#include "init_once.h" 

class Init : public InitOnce<Init> 
{ 
public: 
    Init(); 
    ~Init(); 
}; 
static Init module_init_; 

secondary.cpp:

#include "unit.h" 
extern int i[10]; // the array 

Init::Init() 
{ 
    i[0] = 12345; 
} 
... 
+0

接受。那麼我想做的事情是不可能的。初始化器必須以某種方式從單元外引用。 – adf88 2010-07-07 15:31:07

0

編輯

/*secondary module (secondary.cpp) */ 

    int i[10]; 
    void func() 
    { 
     i[0]=1; 

    } 

/*main module (main.cpp)*/ 

    #include<iostream> 

    extern int i[]; 
    void func(); 
    int main() 
    { 
    func(); 
    std::cout<<i[0]; //prints 1 
    } 

編譯,使用g++ secondary.cpp main.cpp -o myfile

一般構造被使用(並且應當被使用)用於初始化僅一個類的成員鏈路以及創建和執行。

+0

有一個重要性 - 你錯過了我想初始化一個表的單元格。我編輯了我的示例,以便現在可以在代碼中看到它。 – adf88 2010-07-06 15:47:36

+0

@ adf88:編輯我的帖子。 – 2010-07-06 16:05:03

+0

我編輯了我的帖子 - 我更好地解釋了我想寫的「在模塊內部初始化」。 – adf88 2010-07-06 16:28:26

0

我不認爲你想在主模塊中使用extern int i[10];,但是,adf88。

0

這可能有用,但它很危險。單個模塊中的全局/靜態構造順序未定義,模塊加載順序也是未定義的(除非您明確地管理它)。例如,你假設在secondary.c Initialize()運行期間,我已經存在。您必須非常小心,不要讓兩個模塊初始化相同的公共數據,或者讓兩個模塊執行具有重疊副作用的初始化。

我認爲一個更清潔的設計來解決這樣的需求是讓公共數據(您的主要模塊)的所有者將其公開爲全局單例,並具有一個接口來執行所需的任何數據初始化。你可以有一箇中心位置來控制init-order,甚至可以控制併發訪問(使用臨界區或其他併發原語)。沿着你簡化的例子的線,這可能是 -

/主模塊(main.c中)/

的#include 類CommonDat { INT I;

public: 
    const int GetI() { return i;} 
    void SetI(int newI) { i = newI; } 
    void incI()   
    { 
     AcquireSomeLock(); 
     i++; 
     ReleaseTheLock(); 
    } 
} 

CommonDat g_CommonDat; 
CommonDat* getCommonDat() { return &g_CommonDat; } 

int main(void) 
{ 
    printf("%d",getCommonDat()->GetI()); 
} 

這也是優選的是使次級模塊在運行時控制的時間(和期間全局c'tors傳遞)調用這些接口。 (注:您將文件命名爲C文件,但將問題標記爲C++,當然,建議的代碼是C++)。

+0

正如我寫的「在運行main之前不會讀取數組」,所以初始化順序沒有問題。我不想要一箇中心位置來初始化。我想要從關聯模塊觸發每個次要初始化。 – adf88 2010-07-06 16:23:11

+0

這不僅是一個初始化順序問題 - 你能保證在輔助模塊初始化過程中數組甚至被分配*嗎? – 2010-07-06 16:35:15

+0

單個翻譯單元內的初始化順序根據3.6.2進行了很好的定義。我不確定你所說的模塊是什麼 - 所以這可能只是一個關於它是如何在更精細的粒度級別(假設'模塊'>翻譯單元)的評論 – 2010-07-06 16:39:53

0

我可以問一下你爲什麼使用數組(運行當你可以使用std::vector

std::vector<int>& globalArray() 
{ 
    static std::vector<int> V; 
    return V; 
} 

bool const push_back(std::vector<int>& vec, int v) 
{ 
    vec.push_back(v); 
    return true; // dummy return for static init 
} 

這個數組在第一次調用函數時被懶惰地初始化。

您可以使用它像這樣:

// module1.cpp 
static bool const dummy = push_back(globalArray(), 1); 

// module2.cpp 
static bool const dummy = push_back(globalArray(), 2); 

似乎更容易,更不容易出錯。直到C++ 0x爲止,它不是多線程兼容的。

+0

這不是重點。有沒有這樣的風險...... – adf88 2010-07-07 15:00:18

+0

如果沒有這樣的風險,這意味着你的模塊已經緊密耦合(即他們需要知道要寫入哪個索引),在這種情況下,你最好寫在一個單一源文件,能夠實際檢查指標是一目瞭然的。我的觀點是關於脫鉤。 – 2010-07-07 16:54:10