4

这是.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:未解析的外部符号“私有:静态 int TransformationViewer_Utility_Logging::Logger::mTresholdSeverity”(?mTresholdSeverity@Logger@TransformationViewer_Utility_Logging@@0HA) ...

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

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

4

3 回答 3

3

下面是它的工作原理。

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

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

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

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

于 2012-09-01T17:08:27.383 回答
0

问题是mThresholdSeverity不是从 DLL 导出的,而是两个访问器是内联定义的,所以无论它们被调用到哪里,它们都必须能够看到mThresholdSeverity. 有两种解决方案:要么mThresholdSeverity从 DLL 导出(抱歉,我不记得怎么做),或者使访问器成为非内联函数,在 DLL 中定义它们,然后从动态链接库。

于 2012-09-01T16:34:51.507 回答
0

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

于 2012-09-01T18:57:46.180 回答