【问题标题】:Destruction of singleton in DLL破坏 DLL 中的单例
【发布时间】:2010-12-11 06:00:27
【问题描述】:

我正在尝试创建一个简单的 Win32 DLL。作为 DLL 和 EXE 之间的接口,我使用 C 函数,但在 DLL 内部,我使用 C++ 单例对象。以下是我的 DLL 实现示例:

// MyDLLInterface.cpp 文件--------------------

#include "stdafx.h"
#include <memory>
#include "MyDLLInterface.h"    

class MySingleton
{
  friend class std::auto_ptr< MySingleton >;
  static std::auto_ptr< MySingleton > m_pInstance;

  MySingleton()
  {
    m_pName = new char[32];
    strcpy(m_pName, “MySingleton”);
  }
  virtual ~ MySingleton()
  {
    delete [] m_pName;
  }

  MySingleton(const MySingleton&);
  MySingleton& operator=(const MySingleton&);

public:
  static MySingleton* Instance()
  {
    if (!m_pInstance.get())
      m_pInstance.reset(new MySingleton);
    return m_pInstance.get();
  }

  static void Delete()
  {
    m_pInstance.reset(0);
  }

  void Function() {}

private:
  char* m_pName;
};

std::auto_ptr<MySingleton> MySingleton::m_pInstance(0);


void MyInterfaceFunction()
{
  MySingleton::Instance()->Function();
}

void MyInterfaceUninitialize()
{
  MySingleton::Delete();
}

// MyDLLInterface.h 文件 --------------------

#if defined(MY_DLL)
  #define MY_DLL_EXPORT __declspec(dllexport)
#else
  #define MY_DLL_EXPORT __declspec(dllimport)
#endif

MY_DLL_EXPORT void MyInterfaceFunction();
MY_DLL_EXPORT void MyInterfaceUninitialize();

我遇到的问题或问题如下:如果我 从我的 EXE 调用 MyInterfaceUninitialize() ExitInstance() ,我有内存泄漏(m_pName 指针)。为什么会这样?看起来 MySingleton 的破坏发生在 EXE 退出后。是否可以提前一点强制DLL或EXE销毁MySingleton,这样我就不需要调用MyInterfaceUninitialize()函数了?

编辑: 感谢您的所有帮助和解释。现在我明白这是一个设计问题。如果我想继续使用当前的解决方案,我需要在我的 EXE 中调用 MyInterfaceUninitialize() 函数。如果我不这样做,也可以,因为单例在离开 EXE 范围时会破坏自身(但我需要忍受令人不安的调试器消息)。避免这种行为的唯一方法是重新考虑整个实现。

我还可以在 Visual Studio 中的 Linker->Input 下将我的 DLL 设置为“延迟加载的 DLL”,以消除令人不安的调试器消息。

【问题讨论】:

  • 可能还应该将您的复制构造函数设为私有:“private: MySingleton(const MySingleton&);”
  • @PaulH:是的,但这不是这里的问题。
  • 为什么会出现这个问题。如果我们明白这将更容易回答您的问题。就我个人而言,我不希望在 main() 退出之前销毁我的 singeltons。

标签: c++ c visual-studio visual-c++ mfc


【解决方案1】:

您可以利用DllMain callback function 在加载/卸载 DLL 或进程/线程附加/分离时采取适当的措施。然后,您可以为每个附加的进程/线程分配对象,而不是使用单例,因为此回调函数是在附加线程的上下文中执行的。考虑到这一点,还请查看Thread Local Storage (TLS)

【讨论】:

  • 请注意来自 MSDN 的警告:“您可以在 DLL 入口点执行的操作受到严重限制。要提供更复杂的初始化,请为 DLL 创建一个初始化例程。您可以要求应用程序调用在调用 DLL 中的任何其他例程之前初始化例程。"
  • @dalle:是的!这也是我关心的问题。我不确定,这是否会使我的界面复杂化。
【解决方案2】:

如果我没有从我的 EXE ExitInstance() 中调用 MyInterfaceUninitialize(),我有内存泄漏(m_pName 指针)。为什么会这样?

这不是泄漏,这是auto_ptrs 应该工作的方式。他们在超出范围时释放实例(在您的情况下是在卸载 dll 时)。

看起来 MySingleton 的破坏发生在 EXE 退出之后。

是的。

是否可以提前一点强制DLL或EXE销毁MySingleton,这样我就不需要调用MyInterfaceUninitialize()函数了?

不调用这个函数。

【讨论】:

  • 感谢您的确认!您知道任何技巧或解决方法吗?
  • Singleton 基本上是全局对象,你会如何期望全局 int 对象在生命周期内表现?不会有什么不同。如果您真的关心为对象释放内存的顺序,请尝试不同的设计(例如:传递状态而不是使用单例)。
【解决方案3】:

老实说,对于您给出的示例,您是否从 ExitInstance 调用 Uninitialize 方法并不重要。是的,调试器会抱怨未释放的内存,但话又说回来,它是一个单例,它的目的是延长持续时间。

只有当 DLL 中有一些状态信息需要在退出时保留,或者如果您要多次动态加载/卸载 DLL,您才需要认真清理。否则,让操作系统在退出时关闭进程就可以了,此时报告的内存泄漏是无关紧要的。

【讨论】:

  • @JMcDaniel:是的,我明白它是如何工作的。但我想知道,这个问题是否有任何干净的解决方案。
  • 问题是报告的内存泄漏,还是只是卸载 DLL 模块的干净方法?正如下面所指出的,DLLMain 机制与您将要达到的自动 un-init 挂钩点差不多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-07
  • 1970-01-01
  • 1970-01-01
  • 2012-08-30
  • 1970-01-01
相关资源
最近更新 更多