静态库中的Singleton类

时间:2016-06-10 05:40:15

标签: c++ design-patterns singleton static-libraries dynamic-library

假设我在静态库中有一个单例类S,它可以与其他动态库链接D1 D2 D3,

所以根据我的理解,类S将在每个D1,D2和D3中有一个单独的实例,即使它不是单例(如全局)也是如此

有没有办法阻止S类的多个副本? 我不能将单例S放在另一个动态库中。

                 Executable
                /   |   \   \
              D1    D2  D3  D4 
               |    |   |
               S    S   S

编辑: 单例S位于一个单独的静态库中,与D1 D2 D3 ...分开链接 单例在堆中分配,只有指针是静态的

static s::instance()
{
    static smart_ptr<S> ptr = NULL;
    if(ptr == NULL) ptr = new S;
    return ptr;
}

EDIT2:

我做了一个简单的测试用例来检查问题 这是一个示例makefile(用.so替换.d)我做了检查,我在Ubuntu和Cygwin上检查过,两个g ++编译器和行为都不同。 cygwin创建了2个不同的对象但是ubuntu创建了1个对象

all: dynamic1 dynamic2 main

static: static.cpp 
    g++ -c -fPIC static.cpp -o obj/static.o
    ar rvs lib/static.a obj/static.o 

dynamic1: static dynamic1.cpp
    g++ -fPIC -shared dynamic1.cpp lib/static.a -o lib/libdynamic1.dll

dynamic2: static dynamic2.cpp
    g++ -fPIC -shared dynamic2.cpp lib/static.a -o lib/libdynamic2.dll

main: dynamic1 dynamic2 main.cpp
    g++ --std=c++11 main.cpp -ldynamic1 -ldynamic2 -o lib/main -L./lib

2 个答案:

答案 0 :(得分:7)

如果您的动态链接器没有损坏,那么您应该没有任何问题。即使每个动态库实际上都包含来自静态库S的目标文件,动态加载器也应该足够聪明,以确定它们对应于相同的符号,并在整个应用程序中始终使用相同的地址。

简而言之,如果您的系统没有被破坏有一个真正的动态加载器,那么这里没有问题

根据你的编辑,我确认上面是它应该在一个美好的世界中的方式以及它在类Unix系统中的方式。你说它适用于Ubuntu,我可以确认它在FreeBSD上是一样的。

但在Windows上,不幸的是它不同。您没有像ld.so这样的真正动态加载器,但您只需要导出函数的地址或DLL中的数据。因此,每个DLL都将使用自己的单例副本,因为每个DLL都包含自己的代码副本并使用它,因为没有全局链接阶段来合并它。

更糟糕的是,我无法想象任何简单的解决方法:静态链接和动态链接有不同的行为句号。这意味着只要在Windows上使用动态链接,单个或任何可以从至少两个不同的DLL访问的共享数据必须位于一个位置,这意味着DLL。

TL / DR:如果你的系统有一个真正的动态加载器(像Unix一样),你不必担心静态或动态链接,加载器(ld.so)会关心它。在Windows上,它没有动态加载程序,只有运行时LoadLibrary API调用,任何共享数据都必须驻留在一个且只有一个模块中。

答案 1 :(得分:2)

单线人有两种实施方式。

1,单实例作为指针,getInstance将在这种情况下从堆动态分配内存。

2,单个实例作为静态成员,并且没有内存分配。

以下链接讨论了静态内存的位置:Where are static variables stored (in C/C++)?

在上述任何一种实现中:如果D1,D2,D3和D4在相同的应用程序中并且因此处理相同(例如,在不同的线程中使用),则它们共享相同的单例。如果D1,D2,D3和D4属于不同的进程,即它们有自己的存储空间,因此不共享相同的单例。