C++单例类和动态链接库

22

我创建了一个包含类的静态库:

class CLog
{
   private:
   CLog();
   ...
   ...
   public:
   static CLog& GetInstance()                                
   {
           static CLog Instance;
           return Instance;
   }
   void Write(char *cpPr);
};
#define Log CLog::GetInstance()

这个库被链接到一个dll和一个主程序。dll是通过LoadLibrary加载的。在这种情况下,显然在主exe和dll中调用Log.Write会创建两个不同的CLog实例。有什么想法可以解决这个问题并仍然提供动态加载dll?

3个回答

17

问题在于每个链接静态库的项目,无论是主程序还是 DLL,都会获得一个单独的静态变量副本。这破坏了创建单例的典型方法。

最简单的解决方法是创建另一个 DLL 来保存单例,而不是静态库。因为只有一个链接器输出将包含静态变量,所以问题得到解决。

在我的情况下,我创建了一个单例管理器,通过唯一的 GUID 标识每个单例,并确保应用程序范围内只存在一个副本。单例管理器作为自己的 DLL 存在。


3

这是一个简单的库,支持在动态库和可执行文件之间共享相同的单例实例。(在win、linux、macOS上测试通过)

要获取类型为T的单例实例,只需使用singleton<T>()即可。

https://github.com/xhawk18/singleton-cpp

#include "singleton-cpp/singleton.h"

MyObject &obj = singleton<MyObject>();

2
我使用的方法是从EXE中导出一个名为GetLogger的函数,该函数提供了指向单例的指针。GetInstance()的实现取决于_USRDLL预处理器定义。当设置了_USRDLL(用于DLL编译)时,GetInstance()调用GetModuleHandle()以获取对EXE的句柄并加载名为GetLogger的函数。以下是基于您的示例的代码:
静态库具有Log.h:
class Log
{
  private:
  Log();

  public:
  ~Log();

  static Log& GetInstance()                                
  {                 
  #ifdef _USRDLL
    typedef Log* (*GetLoggerFn)();
    HMODULE mod = GetModuleHandle( NULL );
    GetLoggerFn getLogger = (GetLoggerFn)::GetProcAddress( mod, "GetLogger" );
    Log* Instance = getLogger();
    return *Instance;
  #else
    static Log Instance;
    return Instance;
  #endif
  }
  void Write(const std::string& str );
};
#define LOG Log::GetInstance()

静态库中有Log.cpp:

#include "Log.h"

void Log::Write(const std::string& str )
{
    std::cout << this << "  " << str << std::endl;
}

Log::Log()
{
}

Log::~Log()
{
    std::cout << "Log destroyed" << std::endl;
}

DLL只在DllMain中有一个日志声明:

#include "../static/Log.h"
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    LOG.Write("Hello from dll");
    return TRUE;
}

EXE的外观如下:

#include "stdafx.h"
#include "../static/Log.h"
#include <Windows.h>
extern "C"
{
    __declspec( dllexport ) Log* GetLogger()
    {
        return &LOG;
    }
}


int _tmain(int argc, _TCHAR* argv[])
{
    LOG.Write("Hello from exe");
    HMODULE mod = ::LoadLibraryA( "../Debug/tdll.dll");
    ::FreeLibrary( mod );
    LOG.Write("unloaded library");
    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接