c++ - Windows 上 C++ 共享库中的多个 Singleton 实例

标签 c++ dll singleton shared

如果我在链接共享库的可执行文件中访问单例,然后在共享库中访问同一个单例,则会创建两个实例。

这是不允许的还是我做错了什么?

下面是一些代码来说明。我创建了一个包含 Singleton.h、Manager.h、Window.h、Window.cpp 和 DeclSpec.h 的共享库

单例模板类:

#ifndef SINGLETON_H
#define SINGLETON_H

#include <memory>
#include <iostream>

template <typename T>
class Singleton
{
private:
    static std::unique_ptr<T> s_instance;

public:
    static T &instance()
    {
        if (!s_instance) {
            std::cout << "Creating..." << std::endl;
            s_instance = std::make_unique<T>();
        }

        return *s_instance;
    }
};

template <typename T>
std::unique_ptr<T> Singleton<T>::s_instance;

#endif //SINGLETON_H

我创建了一个单例的管理器类:

#ifndef MANAGER_H
#define MANAGER_H

#include "Singleton.h"

class Manager : public Singleton<Manager>
{
};

#endif //MANAGER_H

然后我在同一个库中有一个使用管理器的窗口类。

这是 h(DECLSPEC 在 DeclSpec.h 中定义并处理库导出/导入):

#ifndef WINDOW_H
#define WINDOW_H

#include "DeclSpec.h"

class DECLSPEC Window
{

public:
    Window();

};

#endif //WINDOW_H

这是cpp:

#include "Manager.h"
#include "Window.h"

Window::Window()
{
    Manager &m = Manager::instance();
}

最后,我用一个简单的 main.cpp 创建了一个链接到上述共享库的可执行文件:

#include "Manager.h"
#include "Window.h"

int main(void)
{
    Manager &m = Manager::instance();
    Window w;

    return 0;
}

输出:

Creating...
Creating...

单例被创建了两次。

有什么建议吗?

最佳答案

就像任何其他类一样,您需要告诉编译器导出或导入模板类特化。以下是如何将模板特化正确声明为导入或导出。

#ifndef MANAGER_H
#define MANAGER_H

#include "Singleton.h"

// <BEGIN modifications>
// forward declare the Manager class
class Manager;
// delcare your specialization as import or export
// NOTE: this only works on win32.
// For other platforms you would use the 'extern' keyword, and it does not go in the same place as DECLSPEC
template class DECLSPEC Singleton<Manager>;
// <END modifications>

class Manager : public Singleton<Manager>
{
};

#endif //MANAGER_H

这里的目标是用 DECLSPEC 标记 Singleton 的特定特化。我们不想标记模板类本身,因为您可能希望拥有不存在于您的主要可执行文件中的其他类型的单例。

Microsoft 编译器不会实现带有 declspec 标记的模板。其他编译器(clang、gcc)为此使用“extern”关键字。因此,您有责任在您的一个 cpp 文件中显式实例化模板。如果您忘记这样做,您将得到链接器错误,就像您创建了一个类成员函数却忘记实现它一样。

相反,如果您忘记在没有 DECLSPEC 的情况下在代码中的某处引用 Singleton(例如,如果您转发声明该类),您将收到“多个符号已定义”链接器错误。

因此,将以下内容添加到您的 Manager.cpp 中,以强制编译器完全实例化您的 Singleton 的模板特化:

template class Singleton<Manager>;

关于c++ - Windows 上 C++ 共享库中的多个 Singleton 实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26973342/

相关文章:

c++ - ostream_iterator 到字符串而不是 cout?

c++ - 在 VS 中,以编程方式获取 Linker > Additional Library Directories 属性,或获取宏值

c++ - 如何保护 DLL 功能,或获取导入的位置?

Android 单例类和数据库,访问和更新最常用的对象

java - 线程同步和单例问题

java - 在哪里存储 Servlet 的单例?

c++ - 将 unsigned char 转换为 int 和 short

c++ - 随机数生成和封装

c++ - 使用ffmpeg构建opencv出错

c++ - 如何在 C++ 中的同一个 dll 中获取 dll 位置路径?