以下是我的PoC:代码
a.h:它实现了一个单例方法来创建一个实例
#pragma once
class A
{
public:
int a;
static A& Instance() {
static A a;
return a;
}
};
b.h:声明一个函数将尝试在里面创建一个a实例,并输出它的地址。
#pragma once
void test_b();
b.cc:test_b
的实现
#include "b.h"
#include <iostream>
#include "a.h"
void test_b() {
auto &a = A::Instance();
std::cout << "a address in test_b: " << (void *)(&a) << std::endl;
}
和c.cc:test_c
,它们做与test_b
相同的事情,并调用main
中的test_b
和test_c
来检查单例是否工作。
#include <iostream>
#include "a.h"
#include "b.h"
void test_c() {
auto &a = A::Instance();
std::cout << "a address in test_c: " << (void *)(&a) << std::endl;
}
int main() {
test_b();
test_c();
return 0;
}
我使用b.cc在windows(libb.dll(中构建了一个共享库libb.so,并使用c.cc创建了与共享库链接的test_app。
我已经在Linux和Windows(MinGW(中测试了上述代码,但我得到了不同的结果。
在linux下,输出如下:
a address in test_b: 0x601174
a address in test_c: 0x601174
在MinGW下,输出如下:
a address in test_b: 0x7ff87df93070
a address in test_c: 0x7ff731ef30b0
我用于生成的生成文件。
Makefile:
test_app: c.cc libb.so
g++ -o $@ $^ -lb
libb.so: a.h b.cc
g++ -o $@ -fPIC -shared b.cc
Makefile.ming
test_app.exe: c.cc libb.dll
g++ -o $@ $^ -lb -L.
libb.dll: a.h b.cc
g++ -o $@ -fPIC -shared b.cc
我知道在头文件中实现Singleton不是一个好的做法,但如果有人能帮助解释为什么?
就语言而言,A::Instance
返回的左值必须始终引用程序执行中的同一对象。如果一个语言实现偏离了这一点,那么它就不符合标准。
为什么?
您使用的共享库是该语言的扩展。正如您所看到的,使用这种语言扩展会导致语言实现偏离标准提供的保证。
您可以通过将实例getter定义为单个翻译单元中的非内联函数,而不是内联函数来解决这个问题。另一种方法是根据语言实现的文档(MSVC中的dllexport
、dllimport
(,使用特定于语言实现的函数属性来控制共享库行为。
旁注:Singleton模式要求封装类,以便除了静态实例之外,不能创建任何实例。这个例子只是一个静态的本地对象,从技术上讲不是一个单例,因为构造函数没有封装。