1. 程式人生 > >跨dll中使用單例 不要使用模板

跨dll中使用單例 不要使用模板

(轉載請註明原創於潘多拉盒子)

C++的模板可以幫助我們編寫適合不同型別的模板類,給程式碼的複用性提供了極大的方便。近來寫了一個涉及單例的C++模板類,簡化下來可以歸結為以下的程式碼:

1 2 3 4 5 6 7 8 9 10 11 template <typename T> class Singleton { public: // 此處省去了多執行緒安全鎖 static T* getInstance() { static T t; return &t;   } };

  那麼如果希望對某個work horse類,比如叫做Foo,定義一個Singleton,就會很容易啦:

1 Foo* foo = Singleton<Foo>::getInstance();

 注意這裡不需要自己釋放foo,因為它不是new出來的。

如果程式碼被編譯成“一個”so(dll)或可執行檔案,這裡的Singleton得到的物件卻是是單例的,也就是說,某一種型別得到的物件地址是確定的。

但是,如果同一個型別的單例在不同的so(dll,可執行檔案)中使用,那麼得到的同一個型別的單例物件,其地址也是不一樣的。比如

libfoo.so檔案中的如下程式碼:

1 2 Foo* foo = Singleton<Foo>::getInstance();
std::type_info fooType = typeid(Singleton<Foo>);

  和libbar.so中的另一端程式碼:

1 2 3 Foo* bar = Singleton<Foo>::getInstance(); std::type_info barType = typeid(Singleton<Foo>);

  其中foo和bar的地址是不同的!他們並不是真正的單例。

為什麼呢?原因是模板是編譯的時候例項化成“真正的類”的,而在兩個不同的so編譯生成的過程中,編譯器進行了兩個不同的例項化過程,他們被例項化成了不同的類。也不是完全不同,但有部分是不同的。

比如,對上述兩個so,如果去測試兩個Singleton型別是否為同一型別(RTTI):

1 if (fooType == barType)

  那麼該測試會返回false。但是,如果測試兩個型別的名字是否相等:

1 if (strcmp(fooType.name(), barType.name()) == 0)

  該測試則會返回true。

這說明,兩個型別雖然type_info不同,但名字卻是相同的。

在Google上搜了一下相關的資訊,發現這確實是一個難題。因此,用模板來實現單例,是無法跨so的。當然,這裡不是真正用模板來實現單例,只是用這個例子來演示模板的RTTI特性。