2012-06-11 94 views
1

有很多關於C++模板類的問題,它們包含靜態成員變量以及從動態庫或共享對象中導出它們。但是這個更深入一點:如果有多個共享對象應該怎麼做,每個共享對象都有自己的一組實例,但可能使用來自另一個共享對象的實例?跨多個DLL/DSO的靜態成員的模板類

考慮下面的示例代碼:

/* file: common.h */ 

#include <stdio.h> 
#define PRINT fprintf (stderr, "(template %d) %d -> %d\n", parameter, data, new_data) 

template <int parameter> 
class SharedClass 
{ 
    static int data; 
public: 
    static void Set(int new_data) { PRINT; data = new_data; } 
}; 

template <int parameter> 
int SharedClass<parameter>::data = parameter; 


/* file: library1.h */ 

extern template class SharedClass<1>; 
void Library1Function(); 


/* file: library1.cpp */ 

#include "common.h" 
#include "library1.h" 
#include "library2.h" 

template class SharedClass<1>; 

void Library1Function() 
{ 
    SharedClass<1>::Set (100); 
    SharedClass<2>::Set (200); 
} 


/* file: library2.h */ 

extern template class SharedClass<2>; 
void Library2Function(); 


/* file: library2.cpp */ 

#include "common.h" 
#include "library1.h" 
#include "library2.h" 

template class SharedClass<2>; 

void Library2Function() 
{ 
    SharedClass<1>::Set (1000); 
    SharedClass<2>::Set (2000); 
} 


/* file: main.cpp */ 

#include "common.h" 
#include "library1.h" 
#include "library2.h" 

int main() 
{ 
    Library1Function(); 
    Library2Function(); 
    SharedClass<1>::Set (-1); 
    SharedClass<2>::Set (-2); 
} 

讓我們再假設,我們建立了兩個庫,並使用GCC的應用程序:

$ g++ -fPIC -fvisibility=default -shared library1.cpp -o lib1.so 
$ g++ -fPIC -fvisibility=default -shared library2.cpp -o lib2.so 
$ g++ -fvisibility=default main.cpp -o main -Wl,-rpath=. -L. -l1 -l2 

然後運行可執行文件,我們會得到以下結果:

$ ./main 
(template 1) 1 -> 100 
(template 2) 2 -> 200 
(template 1) 100 -> 1000 
(template 2) 200 -> 2000 
(template 1) 1000 -> -1 
(template 2) 2000 -> -2 

這意味着庫和可執行文件都訪問相同的每個模板靜態存儲。
如果我們運行上的二進制文件「納米-C」,我們會發現每個靜態成員的定義只有一次,在相應的庫:

$ nm -C -A *.so main | grep ::data 
lib1.so:0000000000001c30 u SharedClass<1>::data 
lib2.so:0000000000001c30 u SharedClass<2>::data 

但我有一些問題。

  1. 爲什麼,如果我們從兩個頭取出extern template class ...,我們會看到,靜態成員在每個二進制,但測試的應用程序將繼續正常工作?

    $ nm -C -A *.so main | grep ::data 
    lib1.so:0000000000001c90 u SharedClass<1>::data 
    lib1.so:0000000000001c94 u SharedClass<2>::data 
    lib2.so:0000000000001c94 u SharedClass<1>::data 
    lib2.so:0000000000001c90 u SharedClass<2>::data 
    main:0000000000401e48 u SharedClass<1>::data 
    main:0000000000401e4c u SharedClass<2>::data 
    
  2. 是否有可能在MSVC下構建它?
    或者更具體地說,如何處理__declspec(dllexport)__declspec(dllimport)來使一些實例化出口,還有一些 - 導入?

  3. 最後:這是一個未定義行爲的例子嗎?

回答

1

要回答第1點:當動態連接器解析符號,它採用模塊來鏈接的列表。首先檢查加載的第一個模塊,然後檢查第二個模塊,依此類推。

IIRC,當data構件在主,lib1.so,和lib2.so使用的,這仍然視爲動態符號引用,即使部件被同一模塊中的聲明。因此,當鏈接程序在運行程序時解析符號時,所有三個模塊都會在以下三個模塊中的一個模塊中使用data成員實現:無論哪個先載入。另外兩對仍然被加載到內存中,但未被使用。

(嘗試在所有三個模塊std::cout << &(SharedClass<n>::data) << std::endl;印刷應該是所有6例相同的地址。)

要回答3點,我不認爲這種行爲是不確定的。到底發生了什麼取決於你的系統的動態鏈接器,但我不知道任何鏈接器不會以完全相同的方式處理這種情況。

我不能說第2點,因爲我沒有很多MSVC的經驗。

+0

是的,兩種情況下的地址都是相同的。謝謝。 – intelfx