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

mrz*_*zli 4 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; }
};
Run Code Online (Sandbox Code Playgroud)

和.cpp:

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

int Logger::mTresholdSeverity = 200;

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

void Logger::logFormat(const char* format, ...)
{
    //...
}
Run Code Online (Sandbox Code Playgroud)

我收到此错误:
错误 LNK2001:无法解析的外部符号“私有:静态 int TransformationViewer_Utility_Logging::Logger::mTresholdSeverity”(?mTresholdSeverity@Logger@TransformationViewer_Utility_Logging@@0HA) ...

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

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

Pav*_*sky 5

下面是它的工作原理。

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

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

为了使您编写的内容能够编译,所有用户二进制文件中必须有一处存在 CPP 的定义。这意味着,DLL 和 DLL 的用户(EXE 或 DLL)必须拥有一个具有此定义的 CPP 文件。

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

  • 由于@PavelRadzivilovsky 现在已经认识到他的信息已经过时了二十年,因此您可以忽略他在这里所说的一切。 (2认同)