c++ - 静态变量在 .dll/.lib 中显示不同的行为
问题描述
我试图了解静态在 dll/lib 中的工作原理,所以我做了以下测试:
创建三个项目:
1) libadd //has an class Object and a template class Single<T>
2) libuseadd //has a function useadd which will call
// Single<Object>::instance in libadd
3) app // main() will call
// Single<Object>::instance in libadd and
// useadd in libuseadd
//===========libadd========================
//add.h
#pragma once
#include <iostream>
class __declspec(dllexport) Object
{
public:
Object():cnt_(0){}
void speak()
{
std::cout<<"object:"<<cnt_++<<std::endl;
}
private:
int cnt_;
};
template<typename T>
class __declspec(dllexport) Single
{
public:
static T& instance()
{
static T t;
return t;
}
};
//add.cpp
#include "add.h"
//CMakelists.txt
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
set(PORJ_NAME libadd)
project(${PORJ_NAME})
add_library(${PORJ_NAME}
add.h
add.cpp
)
//===========libuseadd========================
//useadd.h
#pragma once
__declspec(dllexport) void useadd();
//useadd.cpp
#include "useadd.h"
#include "add.h"
void useadd()
{
Object& obj = Single<Object>::instance();
obj.speak();
}
//CMakeLists.txt
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
set(PORJ_NAME libuseadd)
project(${PORJ_NAME})
add_library(${PORJ_NAME}
useadd.h
useadd.cpp
)
target_include_directories( ${PORJ_NAME}
PRIVATE
add/
PUBLIC
)
target_link_libraries (${PORJ_NAME}
PRIVATE
bin/libadd.lib
)
//=====================main.cpp=====================
#include "add.h"
#include "useadd.h"
int main(int argc, char* argv[])
{
Object& obj1 = Single<Object>::instance();
obj1.speak();
useadd();
return 0;
}
//CMakelists.txt
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
set(PORJ_NAME app)
project(${PORJ_NAME})
add_executable(${PORJ_NAME}
main.cpp
)
target_include_directories( ${PORJ_NAME}
PRIVATE
add
useadd
PUBLIC
)
target_link_libraries (${PORJ_NAME}
PRIVATE
libadd.lib
libuseadd.lib
)
如果结果是:
对象:0
对象:1
那么这意味着 instance() 函数中的静态变量只有一个实例(正确)。
如果结果是:
对象:0
对象:0
那么这意味着 instance() 函数中的静态变量是重复的(错误!)
我发现结果取决于库是编译为 dll 还是 lib。当libadd 和libuseadd 都编译为dll 时,甚至取决于实例函数在.h 或cpp 中的实现。假设我们必须将 add.h/add.cpp 更改为:
//when libadd/libuseadd all are dll, we have to use the codes below to
//make it work correctly.
template<typename T>
class __declspec(dllexport) Single
{
public:
static T& instance();
};
template class Single<Object>;
//add.cpp
#include "add.h"
#include "add.h"
template<typename T>
T& Single<T>::instance()
{
static T t;
return t;
}
我想知道为什么
更新(删除模板和类对象):
//===========libadd========================
//add.h
#pragma once
#include <iostream>
class __declspec(dllexport) Single
{
public:
static int& instance()
{
static int t = 0;
int tmp = t++;
return tmp;
}
};
//add.cpp
#include "add.h"
//===========libuseadd========================
//useadd.h
#pragma once
__declspec(dllexport) void useadd();
//useadd.cpp
#include "useadd.h"
#include "add.h"
#include <iostream>
using namespace std;
void useadd()
{
cout<< Single::instance() <<endl;
}
//===========main.cpp=======================
//main.cpp
#include "add.h"
#include "useadd.h"
using namespace std;
int main(int argc, char* argv[])
{
cout << Single::instance() << endl;
useadd();
return 0;
}