未解析的外部符号静态变量(标头中定义的方法使用的变量)

时间:2012-09-01 16:20:24

标签: c++ linker

这是.h:

class Logger
{
private:
    static int mTresholdSeverity;

public:
    static __declspec(dllexport) void log(const char* message);
    static __declspec(dllexport) void logFormat(const char* format, ...);

    static __declspec(dllexport) int getTresholdSeverity() { return mTresholdSeverity; }
    static __declspec(dllexport) void setTresholdSeverity(int tresholdSeverity) { mTresholdSeverity = tresholdSeverity; }
};

和.cpp:

#include "Logger.h"
#include <cstdarg>

int Logger::mTresholdSeverity = 200;

void Logger::log(const char* message)
{
    //...
}

void Logger::logFormat(const char* format, ...)
{
    //...
}

我收到此错误:
错误LNK2001:未解析的外部符号“private:static int TransformationViewer_Utility_Logging :: Logger :: mTresholdSeverity”(?mTresholdSeverity @ Logger @ TransformationViewer_Utility_Logging @@ 0HA)...

显然,mTresholdSeverity已初始化。如果我注释掉getTresholdSeverity()和setTresholdSeverity(),或者我将它们的定义移动到.cpp文件中,则会删除该错误。

为什么在头文件(getTresholdSeverity()或setTresholdSeverity())中定义的静态方法使用静态变量(mTresholdSeverity)时会出现链接错误?

3 个答案:

答案 0 :(得分:2)

以下是它的工作原理。

每个DLL(或EXE)或其他完整的“完全链接”二进制文件必须包含所有引用名称的定义,包括静态变量,包括C ++类中的静态数据成员。

它们将在应用程序中的DLL之间分开。这意味着,此变量值将有所不同,具体取决于您查找的DLL。将函数移动到CPP文件将使它们执行不同的事情:它们现在将看到DLL的变量副本而不是EXE的副本。

要编写您编写的内容,必须在一个位置的所有用户二进制文件中存在CPP中的定义。这意味着,DLL和DLL的用户(EXE或DLL)必须具有一个具有此定义的CPP文件。

这是一个非常大的麻烦,因为除其他外,它使单例模式不可能(为程序中的所有用户提供共享数据对象),并且DLL的每个副本必须具有其自己的静态。此问题仅存在于Windows上,其中DLL是动态* 加载* 库。在UNIX系统上,有一种称为共享对象(SO)的不同技术。它们支持真正的动态链接,这意味着,运行链接器以在运行时解析外部名称。

答案 1 :(得分:0)

问题是mThresholdSeverity不是从DLL导出的,而是两个访问器是内联定义的,因此无论何时调用它们,它们都必须能够看到mThresholdSeverity。有两个解决方案:从DLL导出mThresholdSeverity(抱歉,我不记得如何做到这一点),或者使访问器非内联函数,在DLL中定义它们,并从DLL中导出它们。

答案 2 :(得分:0)

这样不会在噪音中丢失:您可以将访问者更改为普通的非内联函数,并将它们定义在与静态数据成员相同的源文件中。只要你导出它们,你就可以从任何地方调用它们,并且它们可以很好地访问静态数据。