这是.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)时,为什么会出现链接错误?
下面是它的工作原理。
每个 DLL(或 EXE)或其他完整的“完全链接”二进制文件都必须具有所有引用名称的定义,包括静态变量,包括 C++ 类中的静态数据成员。
它们将在应用程序中的 DLL 之间分离。这意味着,这个变量值会有所不同,具体取决于您查看的 DLL。将函数移至 CPP 文件将使它们做不同的事情:它们现在将看到变量的 DLL 副本,而不是 EXE 的副本。
为了使您编写的内容能够编译,所有用户二进制文件中必须有一处存在 CPP 的定义。这意味着,DLL 和 DLL 的用户(EXE 或 DLL)必须拥有一个具有此定义的 CPP 文件。
这是一个非常大的麻烦,因为它使得单例模式变得不可能(为程序内的所有用户提供共享数据对象),并且 DLL 的每个副本都必须有自己的静态状态。这样的问题仅存在于 Windows 上,DLL 是动态加载的库。在 UNIX 系统上,有一种称为共享对象 (SO) 的不同技术。它们支持真正的动态链接,这意味着运行链接器以在运行时解析外部名称。