提出问题
A作为共享库,封装了一个单例类,共享库B和共享库C使用A,D作为可执行程序,使用B和C,那么这个单例是否唯一?
实验
首先创建一个C++项目,项目结构如下
.
├── CMakeLists.txt
├── MeyerSingleton.cpp
├── MeyerSingleton.h
├── testlib.cpp
├── testlibheader1.cpp
├── testlibheader1.h
├── testlibheader2.cpp
└── testlibheader2.h
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
set(CMAKE_CXX_STANDARD 17)
project(test)
// libA
add_library(libA SHARED MeyerSingleton.h MeyerSingleton.cpp)
//libB
add_library(libB SHARED testlibheader1.h testlibheader1.cpp)
target_link_libraries(libB libA)
//libC
add_library(libC SHARED testlibheader2.h testlibheader2.cpp)
target_link_libraries(libC libA)
//exeD
add_executable(testABC testlib.cpp)
target_link_libraries(testABC libB libC)
MeyerSingleton.h
#ifndef MEYER_SINGLETON_H
#define MEYER_SINGLETON_H
class MeyerSingleton
{
private:
MeyerSingleton();
~MeyerSingleton();
MeyerSingleton(const MeyerSingleton&);
MeyerSingleton& operator=(const MeyerSingleton&);
public:
static MeyerSingleton& getInstance();
};
#endif
MeyerSingleton.cpp
#include <iostream>
#include "MeyerSingleton.h"
MeyerSingleton &MeyerSingleton::getInstance()
{
static MeyerSingleton instance;
return instance;
}
MeyerSingleton::MeyerSingleton(){
std::cout<<"MeyerSingleton initialized!"<<std::endl;
}
MeyerSingleton::~MeyerSingleton(){
std::cout<<"MeyerSingleton destroyed!"<<std::endl;
}
testlibheader1和testlibheader2定义了一个函数来调用这个单例,如下
testlibheader1.h
#ifndef TESTLIB_HEADERS_1_H
#define TESTLIB_HEADERS_1_H
namespace tb1{
void test();
}
#endif
testlibheader1.cpp
#include <iostream>
#include <thread>
#include <sstream>
#include "testlibheader2.h"
#include "MeyerSingleton.h"
namespace tb2{
void test(){
std::string msg;
std::stringstream sin;
sin << &MeyerSingleton::getInstance();
sin >> msg;
std::cout << msg << std::endl;
}
}
再在可执行程序中使用这俩个库文件
#include "testlibheader1.h"
#include "testlibheader2.h"
int main(int argc, char **argv){
tb1::test();
tb2::test();
return 0;
}
编译运行结果如下:
MeyerSingleton initialized!
0x1051b8000
0x1051b8000
MeyerSingleton destroyed!
运行结果看起来一切正常。
如果把libA作为静态库呢?于是对CMakeLists.txt作出修改
project(test)
// libA
add_library(libA MeyerSingleton.h MeyerSingleton.cpp) //设定libA为静态库
//libB
add_library(libB SHARED testlibheader1.h testlibheader1.cpp)
target_link_libraries(libB libA)
//libC
add_library(libC SHARED testlibheader2.h testlibheader2.cpp)
target_link_libraries(libC libA)
//exeD
add_executable(testABC testlib.cpp)
target_link_libraries(testABC libB libC)
再次编译运行,结果如下:
MeyerSingleton initialized!
0x1027ec000
MeyerSingleton initialized!
0x102800000
MeyerSingleton destroyed!
MeyerSingleton destroyed!
可以看到,libA中的单例类MeyerSingleton初始化了两次,并且通过libB和libC获取到的单例不是同一个。
接下来将libA中MeyerSingleton::getInstance移动到函数内部
#ifndef MEYER_SINGLETON_H
#define MEYER_SINGLETON_H
class MeyerSingleton
{
private:
MeyerSingleton();
~MeyerSingleton();
MeyerSingleton(const MeyerSingleton&);
MeyerSingleton& operator=(const MeyerSingleton&);
public:
static MeyerSingleton& getInstance()
{
static MeyerSingleton instance;
return instance;
}
};
#endif
再次运行,结果如下
MeyerSingleton initialized!
0x100cac000
0x100cac000
MeyerSingleton destroyed!
看起来问题解决了,再把libA设为动态库后运行结果如下:
MeyerSingleton initialized!
0x10430c000
0x10430c000
MeyerSingleton destroyed!
结论
由此能够得到一个结论,libA作为静态库被libB和libC调用时单例地址可能不一致,经过测试有如下情形:
- 当libA作为静态库且在cpp文件中实现getInstance时,libB和libC同时作为动态库时出现这个问题
- 当libA作为动态库时,libB和libC作为动态库或者静态库时都没有这个问题
所以为了避免这个问题,最好的方式是,将getInstance的实现内联在.h文件中。