0

提前致谢。我现在正在为 C++ 中的大型系统开发一个插件。在我的插件中,我有一些静态变量。我发现当它在 Linux 上以调试模式编译时,它运行良好,没有任何问题。当它在RELEASE模式下编译时,即编译器做了一些优化,然后当我卸载插件时,静态变量没有被删除(静态变量类的析构函数永远不会被调用。)所以内存永远不会释放并且下次我重新加载插件时,它会导致主程序崩溃!

谁能解释一下为什么卸载插件时静态变量没有被破坏?注意:静态变量是静态实例,而不是指针!

class MySettings
{
   public:
      static MySettings& Instance() {
         static MySettings theSingleton;
         return theSingleton;
      }

      virtual ~MySettings();
}

在某处的插件中,它是这样调用的

....
MySettings &s = MySettings::Instance();
s.xxx();
....

当我在调试模式下编译和运行时,我从析构函数中打印了一些信息,当插件被拔出时,看起来实例被正确解构了。但是当我在发布模式下编译和运行时,当插件被拔出时,析构函数永远不会被调用。 我不是插件管理器开发人员,不能说太多。非常感谢你的帮助!

这是加载插件库的一段代码。

newLib._libHandle = ::dlopen(path_to_the_plugin_lib, RTLD_LAZY | RTLD_GLOBAL);
if(! newLib._libHandle) {
  cerr << "dlopen failed for: " << path << " - "
             << ::dlerror();
  return "";

我终于让它工作了。但是还是不明白为什么。这是我所做的:

class MySettings
{
   public:
      static MySettings& Instance() {
         return theSingleton;
      }

   private:
      static MySettings theSingleton;
      virtual ~MySettings();
}

MySettings MySettins:theSingleton;

由于该应用程序非常庞大,包含数百万行代码。我怀疑当 gcc 在RELEASE模式下编译时,优化出了问题。

4

1 回答 1

1

我自己从未尝试过,文档似乎指定静态变量应在重新加载时“重新初始化”。对我来说,它如何与 C++ 的 pre- main()hook 交互并不明显。您可以尝试理解这一点(检查供应商的文档,或者打开二进制文件并查看),但重新设计可能更简单。一些想法:

如果可以的话,摆脱你的单身人士。正如评论中所提到的,这些天的行业共识是,在大多数情况下,单例模式比它的价值更麻烦 - 正如你所发现的那样!公平地说,你不会在 Java 或 Ruby 中遇到这个问题,但仍然存在。

如果您使用 gcc 进行编译,则可以为“dlopen()返回前”和“dlclose()调用后”注册一些钩子。再次从文档中:

...libraries should export routines using the __attribute__((constructor)) and __attribute__((destructor)) function attributes. See the gcc info pages for information on these. Constructor routines are executed before dlopen() returns, and destructor routines are executed before dlclose() returns."

我很确定这是特定于 gcc 的;如果您不使用 gcc,您的平台可能会提供类似的功能。

如果您不能这样做,请尝试切换到单例实现的“首次使用时初始化”样式。这个想法是检测Instance()您的MySettings单例是否已经创建,如果还没有,则首先创建它。就像是:

static MySettings* theSingleton = NULL;
if(theSingleton == NULL)
  theSingleton = new MySettings();
return *theSingleton;

注意这个版本Instance()不是线程安全的如果您愿意,您将不得不遇到一些麻烦。另外:theSingleton永远不会被删除,因此每次重新加载插件时,您的问题都会泄漏一些内存/文件描述符/任何内容。根据您保留的内容类型MySettings,以及您希望用户在进程重新启动之间重新加载插件的频率,这可能会也可能不会接受。

希望这可以帮助。

于 2011-12-02T23:47:23.353 回答