【问题标题】:C++ a singleton class with dllC++ 带有 dll 的单例类
【发布时间】:2011-08-04 02:29:59
【问题描述】:

我用类创建了一个静态库:

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?

【问题讨论】:

    标签: c++ dll static


    【解决方案1】:

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

    解决此问题的最简单方法是创建另一个包含单例的 DLL,而不是静态库。由于只有一个链接器输出将包含静态变量,因此问题解决了。

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

    【讨论】:

      【解决方案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;
      }
      

      【讨论】:

        【解决方案3】:

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

        要获取类型T的单例实例,使用单例()即可。

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

        #include "singleton-cpp/singleton.h"
        
        MyObject &obj = singleton<MyObject>();
        

        【讨论】:

          猜你喜欢
          • 2023-04-04
          • 1970-01-01
          • 2011-09-09
          • 2022-07-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-07-05
          • 1970-01-01
          相关资源
          最近更新 更多