在多个DLL(exe)之间通过指针或者引用传递STL对象时会发现操作失败现象。MSDN上给出了完整的解决方法。在开发应用程序的过程中需要认真对待。
现象:
在不同的DLL或EXE中通过指针或引用操作另一个DLL或EXE中的STL对象时,会遇到严重的程序错误,包括数据错乱或丢失。
原因:
标准C++库的多数类直接或间接的使用了静态数据成员。由于这些类是通过模板创建实例的,所以每个可执行程序(一般是有DLL或EXE扩展名的)包含了有关类静态数据成员的一份Copy。当STL类中的方法要求操作静态数据成员时,这个类操作的静态数据是此方法代码所在的执行程序中的数据。由于静态成员数据在可执行程序中不能保证同步,所以前面提到的操作会导致读取失败或数据混乱和丢失。
解决方法:
1. 在创建STL对象的可执行程序中输出读取的方法(函数)。这些函数包装STL对象要求的功能。这样,STL对象只能在单个可执行程序中被直接读取。例如:假设MyProgram.EXX需要得到MyLibrary.DLL 内deque<MyClass >中的下一个元素,MyLibrary.DLL则要输出读取方法:“MyClass* DequeNextItem (/*...*/);”。MyProgram.EXE就可以执行此方法来得到队列的下一个元素了。
2. 从一个可执行程序中输出模板实例,在另一个可执行程序中引入此实例。例如:MyLibrary.DLL将vector<MyClass>指针回传给MyProgram.EXE中的一个函数,需要在MyLibrary.DLL中输出MyClass类和vector<MyClass>。在MyProgram.EXE中引入它们后。就可以得到MyLibrary.DLL中静态数据成员的一份Copy了。
例子程序:
//---------------------------------------------------------
// AVEXE.CPP
// Compile options needed: /GX
#pragma warning (disable : 4786)
#include <map>
#include <string>
#include <stdio.h>
__declspec(dllimport) std::map<int,std::string>* GiveMeAMap(int n);
__declspec(dllimport) void ShowMeTheMap(std::map<int,std::string> *amap);
__declspec(dllexport) const char* MapItemX (std::map<int,std::string> *m, int x);
int main () {
// Create the map in the DLL
int x = 6;
std::map<int,std::string> *p = GiveMeAMap(x);
// Display the contents of the map from the DLL
printf("Showing contents from the DLL/n");
ShowMeTheMap(p);
// Display the contents of the map from the EXE
// using the accessor function from the DLL so we
// aren't directly accessing the map
printf("Showing contents from the EXE using accessor/n");
int i = x;
while (i--) {
printf("%d = %s/n",i,MapItemX(p,i));
}
// Access Violation when accessing the map that
// was created in the DLL from the EXE
printf("Showing contents from the EXE directly/n");
while (x--) {
printf("%d = %s/n",x,(*p)[x].c_str());
}
return 0;
}
//---------------------------------------------------------
// AVDLL.CPP
// Compile options needed /GX
#pragma warning (disable : 4786)
#include <map>
#include <string>
#include <stdlib.h>
// Create the map here in the DLL
__declspec(dllexport) std::map<int,std::string>* GiveMeAMap(int n)
{
std::map<int,std::string> *m = new std::map<int,std::string>;
while(n--) {
char b[33];
itoa(n,b,2);
(*m)[n] = std::string(b);
}
return m;
}
// We can access the map without error from the executable
// image where the map was created
__declspec(dllexport) void ShowMeTheMap(std::map<int,std::string> *p)
{
int x = p->size();
while (x--) {
printf("%d = %s/n",x,(*p)[x].c_str());
}
}
// An accessor method to return the associated C string
// for key x
__declspec(dllexport) const char* MapItemX (std::map<int,std::string> *m, int x)
{
return (*m)[x].c_str();
}
现象:
在不同的DLL或EXE中通过指针或引用操作另一个DLL或EXE中的STL对象时,会遇到严重的程序错误,包括数据错乱或丢失。
原因:
标准C++库的多数类直接或间接的使用了静态数据成员。由于这些类是通过模板创建实例的,所以每个可执行程序(一般是有DLL或EXE扩展名的)包含了有关类静态数据成员的一份Copy。当STL类中的方法要求操作静态数据成员时,这个类操作的静态数据是此方法代码所在的执行程序中的数据。由于静态成员数据在可执行程序中不能保证同步,所以前面提到的操作会导致读取失败或数据混乱和丢失。
解决方法:
1. 在创建STL对象的可执行程序中输出读取的方法(函数)。这些函数包装STL对象要求的功能。这样,STL对象只能在单个可执行程序中被直接读取。例如:假设MyProgram.EXX需要得到MyLibrary.DLL 内deque<MyClass >中的下一个元素,MyLibrary.DLL则要输出读取方法:“MyClass* DequeNextItem (/*...*/);”。MyProgram.EXE就可以执行此方法来得到队列的下一个元素了。
2. 从一个可执行程序中输出模板实例,在另一个可执行程序中引入此实例。例如:MyLibrary.DLL将vector<MyClass>指针回传给MyProgram.EXE中的一个函数,需要在MyLibrary.DLL中输出MyClass类和vector<MyClass>。在MyProgram.EXE中引入它们后。就可以得到MyLibrary.DLL中静态数据成员的一份Copy了。
例子程序:
//---------------------------------------------------------
// AVEXE.CPP
// Compile options needed: /GX
#pragma warning (disable : 4786)
#include <map>
#include <string>
#include <stdio.h>
__declspec(dllimport) std::map<int,std::string>* GiveMeAMap(int n);
__declspec(dllimport) void ShowMeTheMap(std::map<int,std::string> *amap);
__declspec(dllexport) const char* MapItemX (std::map<int,std::string> *m, int x);
int main () {
// Create the map in the DLL
int x = 6;
std::map<int,std::string> *p = GiveMeAMap(x);
// Display the contents of the map from the DLL
printf("Showing contents from the DLL/n");
ShowMeTheMap(p);
// Display the contents of the map from the EXE
// using the accessor function from the DLL so we
// aren't directly accessing the map
printf("Showing contents from the EXE using accessor/n");
int i = x;
while (i--) {
printf("%d = %s/n",i,MapItemX(p,i));
}
// Access Violation when accessing the map that
// was created in the DLL from the EXE
printf("Showing contents from the EXE directly/n");
while (x--) {
printf("%d = %s/n",x,(*p)[x].c_str());
}
return 0;
}
//---------------------------------------------------------
// AVDLL.CPP
// Compile options needed /GX
#pragma warning (disable : 4786)
#include <map>
#include <string>
#include <stdlib.h>
// Create the map here in the DLL
__declspec(dllexport) std::map<int,std::string>* GiveMeAMap(int n)
{
std::map<int,std::string> *m = new std::map<int,std::string>;
while(n--) {
char b[33];
itoa(n,b,2);
(*m)[n] = std::string(b);
}
return m;
}
// We can access the map without error from the executable
// image where the map was created
__declspec(dllexport) void ShowMeTheMap(std::map<int,std::string> *p)
{
int x = p->size();
while (x--) {
printf("%d = %s/n",x,(*p)[x].c_str());
}
}
// An accessor method to return the associated C string
// for key x
__declspec(dllexport) const char* MapItemX (std::map<int,std::string> *m, int x)
{
return (*m)[x].c_str();
}