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

时间:2014-11-17 13:03:21

标签: c++ dll singleton shared

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

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

这里有一些代码可供说明。我创建了一个包含Singleton.h,Manager.h,Window.h,Window.cpp和DeclSpec.h的共享库

Singleton模板类:

#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...

单身人士被创造两次。

任何指针?

1 个答案:

答案 0 :(得分:0)

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

#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)使用&#39; extern&#39;用于此目的的关键字。因此,您有责任在其中一个cpp文件中显式实例化模板。如果您忘记这样做,您将获得链接器错误,就像您创建了类成员函数并忘记实现它一样。

相反,如果你忘记在没有DECLSPEC的情况下在代码中的某处引用Singleton(就像你转发声明该类一样),你将得到多个符号定义&#39;链接器错误。

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

template class Singleton<Manager>;