C++ 静态变量在.dll/.lib中显示不同的行为
我试图了解静态在dll/lib中是如何工作的,因此我做了以下测试: 创建三个项目:C++ 静态变量在.dll/.lib中显示不同的行为,c++,visual-c++,dll,C++,Visual C++,Dll,我试图了解静态在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 li
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
//=============libuseadd=======================
//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"
//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();
}
#include "add.h"
#include "useadd.h"
int main(int argc, char* argv[])
{
Object& obj1 = Single<Object>::instance();
obj1.speak();
useadd();
return 0;
}
//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"
//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
#include "add.h"
#include "useadd.h"
using namespace std;
int main(int argc, char* argv[])
{
cout << Single::instance() << endl;
useadd();
return 0;
}
//==================================main.cpp=====================
//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"
//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();
}
#include "add.h"
#include "useadd.h"
int main(int argc, char* argv[])
{
Object& obj1 = Single<Object>::instance();
obj1.speak();
useadd();
return 0;
}
//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"
//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
#include "add.h"
#include "useadd.h"
using namespace std;
int main(int argc, char* argv[])
{
cout << Single::instance() << endl;
useadd();
return 0;
}
如果结果是:
对象: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/libuseadd都是dll时,我们必须使用下面的代码
//让它正常工作。
模板
类declspec(dllexport)单个
{
公众:
静态T&实例();
};
模板类单一;
//add.cpp
#包括“add.h”
#包括“add.h”
模板
T&Single::instance()
{
静态T;
返回t;
}
我想知道为什么
更新(删除模板和类对象):
//================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"
//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();
}
#include "add.h"
#include "useadd.h"
int main(int argc, char* argv[])
{
Object& obj1 = Single<Object>::instance();
obj1.speak();
useadd();
return 0;
}
//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"
//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
#include "add.h"
#include "useadd.h"
using namespace std;
int main(int argc, char* argv[])
{
cout << Single::instance() << endl;
useadd();
return 0;
}
//add.h
#布拉格语一次
#包括
类declspec(dllexport)单个
{
公众:
静态int&实例()
{
静态int t=0;
int tmp=t++;
返回tmp;
}
};
//add.cpp
#包括“add.h”
//=============libuseadd=======================
//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"
//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();
}
#include "add.h"
#include "useadd.h"
int main(int argc, char* argv[])
{
Object& obj1 = Single<Object>::instance();
obj1.speak();
useadd();
return 0;
}
//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"
//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
#include "add.h"
#include "useadd.h"
using namespace std;
int main(int argc, char* argv[])
{
cout << Single::instance() << endl;
useadd();
return 0;
}
//useadd.h
#布拉格语一次
__declspec(dllexport)void useadd();
//useadd.cpp
#包括“useadd.h”
#包括“add.h”
#包括
使用名称空间std;
void useadd()
{
尝试从DLL导出模板类型通常是不明智的。客户端代码可以使用它想要的任何类型,DLL作者无法预测它将使用哪些类型。这并不是禁止的,在某些情况下可能有用。一个很好的例子是std::basic_string
,msvcp.DLL将char和wchar_____T专门化导出到实现t std::string和std::wstring。静态成员不可避免地会将其变成毒药,不要这样做。@AlexF谢谢!我删除了模板和类对象。但是您可以看到,我仍然可以复制奇怪的behavior@HansPassant感谢您的澄清!您知道为什么静态变量在dll和库中的行为不同吗?当您链接静态库时通常,每个模块都有自己的副本,并且保证不会工作。当您使用DLL时,它可能会工作,因为只有DLL模块将变量存储在其数据段中。强调可能,您必须正确猜测T。只有当您在模块bou的进程级别上获得运行时支持时,这才能真正达到良好的效果NDARES,仲裁如何存储变量。VM实现的特性,如JVM和CLR,而不是C++。“当你连接静态库时,每个模块都会得到自己的拷贝,并且保证不工作。”在我的测试中,如果libadd和libuseadd都是静态的,那么它可以工作,比如说,只有一个静态变量实例。你知道为什么吗?