关于静态库中使用全局变量可能导致的问题

同事找我看一个问题,一个访问全局变量不符合预期的问题。
因为新工程中静态库动态库非常多,非常不利于分析问题。
再因为并不是一个业务逻辑问题,而是一个语言层面的问题,所以我单独抽象出产生问题的环境,简化问题,更容易分析。
刚开始,是一个方案,五个工程,能够复现问题。
然后继续缩减三个工程,依然能够复现问题。
三个工程分别为静态库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保证只有一份静态库的实现。

个人觉得静态库有太多实现,总感觉不太安全,觉得静态库只有一份实现比较好。
如果有多份实现,最好用动态库。
如果感觉上面比较麻烦,静态库中最好不要有全局变量。

测试代码

posted @ 2016-06-23 16:13  飞鹤0755  阅读(4898)  评论(0编辑  收藏  举报