跨dll中使用單例 不要使用模板
阿新 • • 發佈:2019-02-08
(轉載請註明原創於潘多拉盒子)
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特性。