2009-09-08 102 views
9

我有下一種情況:我需要在獨立靜態庫中創建小部件,然後將它與最終應用程序(visual C++ 9.0,qt 4.5)鏈接。 這個靜態小部件庫包含一些資源(圖標),並由幾個.cpp文件(每個包含獨立小部件)組成。據我所知,我必須初始化qt資源系統,如果我在靜態庫中使用它們(資源),並調用「Q_INIT_RESOURCE(resource_file_name)」。我解決了這個用下面的代碼(在靜態庫中的每個.cpp文件):初始化靜態庫中嵌入的qt資源

 

#include <QAbstractButton> 

namespace { 
struct StaticLibInitializer 
{ 
    StaticLibInitializer() 
    { 
     Q_INIT_RESOURCE(qtwidgets_custom_resources); 
    } 
}; 
StaticLibInitializer staticLibInitializer; 
} 

// ... widget code .... 
 

而不是我的第一個方法,我已經創建單獨的init.cpp文件中的靜態庫項目與初始化代碼(以避免包括初始化代碼在每個.cpp文件中),但是這不起作用。

爲什麼這不起作用?

StaticLibInitializer的這種方法在各種編譯器和平臺之間是安全可移植的嗎?

回答

10

它沒有工作,因爲你設法碰到static initialization order fiasco

您不能移動初始化靜態對象的代碼,將使用這些靜態對象的翻譯單元(可以將其作爲源文件讀取)放大。不是你做到這一點。如果你想使用你用來初始化這些靜態對象的方案,而不是隻將聲明移動到init.hpp頭部,但是在每個使用靜態對象的文件中留下安裝位置StaticLibInitializer staticLibInitializer;
以上建議假定每個小部件僅使用自己的資源。如果你有一個小部件的資源被另一個小部件使用的情況,你會再次遇到靜態初始化順序失敗。您可以通過使用這樣的代碼

StaticLibInitializer 
{ 
    void initialize() 
    { 
     static Q_INIT_RESOURCE(qtwidgets_custom_resources); 
    } 

    StaticLibInitializer() 
    { 
     initialize(); 
    } 
} 

使StaticLibInitializer的確保乘實例將初始化給定的資源只有一次,然後實例StaticLibInitializer爲您將要在給定的翻譯單位要利用一切資源管理這種情況。

+0

在我目前的情況下,我有三個.cpp文件(每個文件都實現了它自己的小部件,其中兩個使用.qrc文件中的資源),但是初始化代碼,我在原始問題中只給出了其中的一個,罰款(100%,而不是50/50)。所以我不明白,爲什麼當我把初始化代碼放在單獨的init中。cpp文件我無法使用我的資源,但是當這個代碼在一個小部件的.cpp文件中都可以正常工作時... – cybevnm 2009-09-15 08:36:40

+0

它沒關係,它現在工作正常**現在** :)它僅在偶然情況下才有效。當你開始使用另一個編譯器甚至是同一個編譯器的另一個版本時,它可能會停止工作。它是**未定義的行爲**。現在它的工作原因是因爲當你在其中一個小部件的文件中有初始化代碼時,編譯器**發生**首先初始化你的資源。純粹的運氣,沒有更多。如果你不想讓你的程序工作0%,一個陽光燦爛的日子按照指示,以避免*靜態初始化順序失敗*。 – 2009-09-15 12:45:34

+0

在編譯階段是由編譯器定義的靜態初始化順序,還是順序可能會在程序重新啓動(不重新編譯)之間變化? – cybevnm 2009-09-15 13:19:49

6

Q_INIT_RESOURCE宏不能在名稱空間中使用。

讓我引用qt手冊:「注意:這個宏不能用在名字空間中,它應該從main()中調用」。即使它給你一個例子,如何做是正確的,如果這是不可能的:

inline void initMyResource() { Q_INIT_RESOURCE(myapp); } 

    namespace MyNamespace 
    { 
    ... 

    void myFunction() 
    { 
     initMyResource(); 
    } 
    } 

請看看自己,爲什麼,以及如何準確地失敗,或者如果您在未指定的方式來使用它不會失敗。相關代碼在QtCore中。

+0

但是在第一種方法中(當我在靜態庫的每個.cpp文件中包含代碼時)都可以使用(即使使用匿名命名空間)。 – cybevnm 2009-09-08 09:29:31

+0

上面使用'inline'不會給你任何東西,因爲你不能保證它會被編譯器所尊重。 *不尊重這個關鍵字符合C++標準。因此,如果這個*解決方案*是基於假設內聯函數將被內聯,那麼它將被破壞。 – 2009-09-14 07:55:02

+1

「內聯」函數具有稍微不同的語義,尤其是涉及到ODR時。考慮到我們不知道所有平臺上的'Q_INIT_RESOURCE'的宏觀擴展,很難判斷它是否需要。把它放在那裏當然是合理的。 – MSalters 2009-09-14 08:39:30