0

我觉得这个问题可能有一个简单的解决方案,但对我来说并不明显——我有一个配置类,用于存储从 ini 文件加载的各种配置选项,以及其他地方。在我的应用程序中,我有一个库和客户端,以及 2 个配置 - 将库构建为 DLL 并动态链接客户端,或者将它们一起构建为单个二进制文件。那么如何在库和客户端中拥有/使用我的配置对象呢?如果我在两者中都包含配置类定义,我认为它会由于重新定义而给我链接错误。

4

1 回答 1

0

如果我在两者中都包含配置类定义,我认为它会由于重新定义而给我链接错误。

不,不会的。Windows DLL 不遵守单一定义规则。基本上,在 Windows 中,ODR 停止在模块边界,即 ODR可执行文件和DLL 中受到尊重,但不在它们之间这是否是一件好事并不重要,事情就是这样。因此,您可以在 DLL 和可执行文件中包含配置类的定义。但是,将有两个单独的单例实例(我假设它是一个单例类,就像配置类通常那样),每个模块中都有一个。从这个意义上说,它不会是真正的单例,至少不会跨模块。

如果你想要一个真正的跨模块的单例,你必须做更多的工作。您有两个选择:主从或合并(或“菊花链”)。

第一种选择是将一个模块(例如,可执行文件)指定为实例化(并保留)单例对象的模块,然后将指向该实例的指针传递给所有“从属”模块,然后这些“从属”模块可以通过公共接口(因此两个模块对配置类具有相同的声明,但只有一个模块创建它并将其传递给其他模块)。它看起来像这样:

在头文件“config_class.h”中:

class ConfigClass {

  // a bunch of declarations...

  public:
    static ConfigClass& getInstance();  // the access-point for the singleton.
};

#ifdef MY_LIB_NOW_BUILDING_MASTER

extern "C" __declspec(dllimport) void setConfigClassInstance(ConfigClass* pobj);

#else

extern "C" __declspec(dllexport) void setConfigClassInstance(ConfigClass* pobj);

#endif 

在 cpp 文件“config_class.cpp”中:

#include "config_class.h"

// a bunch of definitions for the config_class member functions.

#ifdef MY_LIB_NOW_BUILDING_MASTER

ConfigClass& ConfigClass::getInstance() {
  static ConfigClass instance( /*  */ );
  return instance;
};

#else

static ConfigClass* masterInstance;

void setConfigClassInstance(ConfigClass* pobj) {
  masterInstance = pobj;
};

ConfigClass& ConfigClass::getInstance() {
  return *masterInstance;
};

#endif

在上面的示例中,您将从主模块(很可能是主可执行文件)调用 setConfigClassInstance 来设置 DLL 的配置对象,但要确保 DLL 在静态初始化(加载)期间不需要配置类.

第二种选择是合并或菊花链您的单例。在这种情况下,每个模块都会创建自己的单例实例,但随后,使用与上述类似的方案,它们将指针传递给彼此的实例,从而允许它们合并到一个实例中(交叉链接),或者将它们链接在一起(例如,像循环链表或环表),然后您将调用分派到适当的实例。

我认为对于您的应用程序,第一个选项可能是最简单的。

注意:在非 Windows 环境中,情况完全不同,以上均不适用。

于 2012-12-15T06:03:52.683 回答