關於靜態庫中使用全局變量可能導致的問題

同事找我看一個問題,一個訪問全局變量不符合預期的問題。
因爲新工程中靜態庫動態庫非常多,非常不利於分析問題。
再因爲並不是一個業務邏輯問題,而是一個語言層面的問題,所以我單獨抽象出產生問題的環境,簡化問題,更容易分析。
剛開始,是一個方案,五個工程,能夠復現問題。
然後繼續縮減三個工程,依然能夠復現問題。
三個工程分別爲靜態庫A,DLL B,EXE C。三者的依賴關係爲:B依賴A, C依賴A和B。

複製代碼
工程A的主要實現代碼:
int g_int = 0; // 全局變量
int CStaticClass::GetGlobalValue()
{
return g_int;
}
工程B的主要實現代碼:
// DLL.H
class DLL_API CDLL {
}
// DLL.CPP
extern int g_int;
CDLL::CDLL(void)
{
m_pMyClass = new CMyClass();
m_pStatic = new CStaticClass();
g_int = 1;
}

CDLL::~CDLL(void)
{
delete m_pMyClass;
m_pMyClass = NULL;
}

CMyClass* CDLL::GetClassPtr()
{
return m_pMyClass;
}
工程C的主要實現代碼:
extern int g_int;
int _tmain(int argc, _TCHAR* argv[])
{
g_int = 2;

CDLL dll;
CMyClass* pClass = dll.GetClassPtr();
int n = pClass->Get(); // 這裏的n爲2,即不是1,也不是0
}
複製代碼

使用最少的代碼復現問題,可以將問題集中在更小的方面,便於分析。
工程還可以進一步簡化,手動將靜態庫中的類CStaticClass在兩個工程B, C中實現。
然後調試代碼時,進入int n = pClass->Get(); 進入Get()函數實現裏,我們可能看到進入的是EXE工程的實現代碼。
雖然指針是從DLL中導出來的,但是調用的卻是EXE中的實現代碼。
爲什麼?因爲DLL和EXE都是獨立的可執行代碼。
如果DLL導出了CMyClass類,且EXE中沒有CMyClass的實現代碼,自然會去調用DLL中的實現代碼。
如果DLL沒有導出CMyClass類,且EXE中有CMyClass類的實現代碼,那麼自然會調用EXE中的實現代碼。
如果DLL導出了CMyClass類,且EXE中也有CMyClass類的實現代碼,則鏈接的時候會報重複定義的錯誤。
所以,如果調用的是靜態庫中的類函數的實現,則自然使用DLL中的全局變量。
如果調用的是EXE中的實現,則自然是訪問EXE中的全局變量。
問題的解決方案:
方案1、靜態庫中去掉全局變量,改用其他方式。
方案2、靜態庫改成動態庫。
方案3、整個Solution保證只有一份靜態庫的實現。

個人覺得靜態庫有太多實現,總感覺不太安全,覺得靜態庫只有一份實現比較好。
如果有多份實現,最好用動態庫。
如果感覺上面比較麻煩,靜態庫中最好不要有全局變量。

測試代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章