在一個mfc擴展動態庫導出函數返回了map變量 ,在exe使用該變量會導致崩潰。如果直接調用該變量不通過函數也一樣。不要在動態庫接口中傳遞STL容器!!VC的STL版本,編譯選項也是一樣的。針對Vector引用的傳遞沒有問題,但是Map就出現問題了。vc6下、通過“一個exe/dll中的指針”指向或者引用“另一個exe/dll中的、包含stl的類”時、這個包含stl的類在析構時就可能出現這樣的問題。 1、微軟的解釋: 大部分C++標準庫裏提供的類直接或間接地使用了靜態變量。由於這些類是通過模板擴展而來的,因此每個可執行映像(通常是. dll或.exe文件)就會存在一份只屬於自己的、給定類的靜態數據成員。當一個需要訪問這些靜態成員的類方法執行時,它使用的是“這個方法的代碼當前所 在的那份可執行映像”裏的靜態成員變量。由於兩份可執行映像各自的靜態數據成員並未同步,這個行為就可能導致訪問違例,或者數據看起來似乎丟失或被破壞 了。 可能不太好懂,我舉個例子:假如類A<T>有個靜態變量m_s,那麼當1.exe使用了2.dll中提供的某個A<int>對象時,由於模板擴展機制,1.exe和2.dll中會分別存在自己的一份類靜態變量A<int>.m_s。 這 樣,假如1.exe中從2.dll中取得了一個的類A<int>的實例對象a,那麼當在1.exe中直接訪問a.m_s時,其實訪問的是 1.exe中的對應拷貝(正確情況應該是訪問了2.dll中的a.m_s)。這樣就可能導致非法訪問、應當改變的數據沒有改變、不應改變的數據被錯誤地更 改等異常情形。 郵件列表裏的Conrad Weyns這樣認為(意譯,就不一句句對應了): 每個.dll或.exe文檔可以看作一個執行單元。而由於stl的特 性,每個執行單元中可能會有一個自己的內存分配器(通俗點説,就是堆內存管理器,或者內存池)。當跨越執行單元調用構造/析構函數時,如果這兩個調用所在 的執行單元不同,就可能出現通過A的堆管理器去釋放B的堆管理器所分配的對象的問題。這就導致了RtlValidateHeap拋出異常。 他所認為的正確解決辦法是:使用各種措施,保證程序中只用了一個堆管理器;或者使用智能型的堆管理器(作者建議使用SmartHeap)。 1、保證資源的分配/刪除操作對等並處於同一個執行單元; 比如,可以把這些操作(包括構造/析構函數、某些容器自動擴容{這個需要特別注意}時的內存再分配等)隱藏到接口函數裏面。換句話説:儘量不要直接從dll中輸出stl對象;如果一定要輸出,給它加上一層包裝,然後輸出這個包裝接口而不是原始接口。 2、保證所有的執行單元使用同樣版本的STL運行庫。 比如,全部使用release庫或debug庫,否則兩個執行單元擴展出來的STL類的內存佈局就可能會不一樣。 參考: http://support.microsoft.com/default.aspx?scid=kb%3ben-us%3b168958
How to export an instantiation of a Standard Template Library (STL) class and a class that contains a data member that is an STL object
View products that this article applies to.
|
Article ID
|
:
|
168958
|
|
Last Review
|
:
|
September 6, 2005
|
|
Revision
|
:
|
3.0
|
This article was previously published under Q168958
On This Page
SUMMARY
MORE INFORMATION
To Export an STL Class
To Export a Class Containing a Data Member that Is an STL Object
Sample Code
REFERENCES
<style>.toc{display: none;}</style>
SUMMARY
|
•
|
Export an instantiation of a Standard Template Library (STL) class.
|
|
•
|
Export a class that contains a data member that is an STL object.
|
Note that you may not export a generalized template. The template must be instantiated; that is, all of the template parameters must be supplied and must be completely defined types at the point of instantiation. For instance "stack<int>;" instantiates the STL stack class. The instantiation forces all members of class stack<int> to be generated.
Also note that some STL containers (map, set, queue, list, deque) cannot be exported. Please refer to the More Information section to follow for a detailed explanation.
Back to the top
MORE INFORMATION
Beginning with Visual C++ 5.0, it is possible to force an instantiation of a template class and export the instantiation. To export a template class instantiation, use the following syntax:
Back to the top
To Export an STL Class
|
1.
|
In both the DLL and the .exe file, link with the same DLL version of the C run time. Either link both with Msvcrt.lib (release build) or link both with Msvcrtd.lib (debug build).
|
|
2.
|
In the DLL, provide the __declspec specifier in the template instantiation declaration to export the STL class instantiation from the DLL.
|
|
3.
|
In the .exe file, provide the extern and __declspec specifiers in the template instantiation declaration to import the class from the DLL. This results in a warning C4231 "nonstandard extension used : 'extern' before template explicit instantiation." You can ignore this warning.
|
Back to the top
To Export a Class Containing a Data Member that Is an STL Object
|
1.
|
In both the DLL and the .exe file, link with the same DLL version of the C run time. Either link both with Msvcrt.lib (release build) or link both with Msvcrtd.lib (debug build).
|
|
2.
|
In the DLL, provide the __declspec specifier in the template instantiation declaration to export the STL class instantiation from the DLL.
NOTE: You cannot skip step 2. You must export the instantiation of the STL class that you use to create the data member. |
|
3.
|
In the DLL, provide the __declspec specifier in the declaration of the class to export the class from the DLL.
|
|
4.
|
In the .exe file, provide the __declspec specifier in the declaration of the class to import the class from the DLL.
If the class you are exporting has one or more base classes, then you must export the base classes as well. If the class you are exporting contains data members that are of class type, then you must export the classes of the data members as well.
|
NOTE: Some STL classes use other STL classes. These other classes must also be exported. The classes that must be exported are listed in compiler warnings if you compile with a warning level lower than 1; that is, /W2, /W3, or /W4. Warning level 4 generates a lot of warning messages for STL headers and is not currently recommended for that reason.
Some STL classes contain nested classes. These classes can not be exported. For instance, deque contains a nested class deque::iterator. If you export deque, you will get a warning that you must export deque::iterator. If you export deque::iterator, you get a warning that you must export deque. This is caused by a designed limitation that once a template class is instantiated, it can not be re-instantiated and exported. The only STL container that can currently be exported is vector. The other containers (that is, map, set, queue, list, deque) all contain nested classes and cannot be exported.
When you export an STL container parameterized with a user-defined type (UDT), you must define the operators < and == for your UDT. For example, if you export vector<MyClass>, you must define MyClass::operator < and MyClass operator ==. This is because all STL container classes have member comparison operators that require the existence of the operators < and == for the contained type. Normally, these are not instantiated because they are not used. When you instantiate an instance of a template class, all member functions are generated. Because the STL container classes have member functions that use the operators < and == for the contained type, you must implement them. If comparing objects of your UDT does not make sense, you can define the comparison operators to simply return "true."
When the symbol _DLL is defined during compiling (this symbol is implicitly defined when compiling with /MD or /MDd to link with the DLL version of the C Runtime), the following STL classes, and various global operators and functions that operate on these classes, are already exported by the C Runtime DLL. Therefore, you cannot export them from your DLL. This should not cause a problem for the executable program that imports your class as long as it also uses the DLL version of the C run time:
Header STL template class
------------------------------
<IOSFWD> basic_ios
<IOSFWD> <IOSFWD>
<IOSFWD> basic_istream
<IOSFWD> basic_string (also typedef'd as string and wstring)
<IOSFWD> complex
<LOCALE> messages
<XLOCALE> codecvt
<XLOCALE> ctype
<XLOCMON> moneypunct
<XLOCMON> money_get
<XLOCMON> money_put
<XLOCNUM> numpunct
<XLOCTIME> time_get
<XLOCTIME> time_put
<XSTRING> basic_string (also typedef'd as string and wstring)
For specific details on which template parameters are used and which global functions and operators are declared, please see the relevant header file.
Back to the top
Sample Code
// -------------------------------------------
// MYHEADER.H
//disable warnings on 255 char debug symbols
#pragma warning (disable : 4786)
//disable warnings on extern before template instantiation
#pragma warning (disable : 4231)
#include <vector>
// Provide the storage class specifier (extern for an .exe file, null
// for DLL) and the __declspec specifier (dllimport for .an .exe file,
// dllexport for DLL).
// You must define EXP_STL when compiling the DLL.
// You can now use this header file in both the .exe file and DLL - a
// much safer means of using common declarations than two different
// header files.
#ifdef EXP_STL
# define DECLSPECIFIER __declspec(dllexport)
# define EXPIMP_TEMPLATE
#else
# define DECLSPECIFIER __declspec(dllimport)
# define EXPIMP_TEMPLATE extern
#endif
// Instantiate classes vector<int> and vector<char>
// This does not create an object. It only forces the generation of all
// of the members of classes vector<int> and vector<char>. It exports
// them from the DLL and imports them into the .exe file.
EXPIMP_TEMPLATE template class DECLSPECIFIER std::vector<int>;
EXPIMP_TEMPLATE template class DECLSPECIFIER std::vector<char>;
// Declare/Define a class that contains both a static and non-static
// data member of an STL object.
// Note that the two template instantiations above are required for
// the data members to be accessible. If the instantiations above are
// omitted, you may experience an access violation.
// Note that since you are exporting a vector of MyClass, you must
// provide implementations for the operator < and the operator ==.
class DECLSPECIFIER MyClass
{
public:
std::vector<int> VectorOfInts;
static std::vector<char> StaticVectorOfChars;
public:
bool operator < (const MyClass > c) const
{
return VectorOfInts < c. VectorOfInts;
}
bool operator == (const MyClass > c) const
{
return VectorOfInts == c. VectorOfInts;
}
};
// Instantiate the class vector<MyClass>
// This does not create an object. It only forces the generation of
// all of the members of the class vector<MyClass>. It exports them
// from the DLL and imports them into the .exe file.
EXPIMP_TEMPLATE template class DECLSPECIFIER std::vector<MyClass>;
// -------------------------------------------
// Compile options needed: /GX /LDd /MDd /D"EXP_STL"
// or: /GX /LD /MD /D"EXP_STL"
// DLL.CPP
#include "MyHeader.h"
std::vector<char> MyClass::StaticVectorOfChars;
// -------------------------------------------
// Compile options needed: /GX /MDd
// or: /GX /MD
// EXE.CPP
#include <iostream>
#include "MyHeader.h"
int main ()
{
MyClass x;
for (int i=0; i<5; i++) x.VectorOfInts.push_back(i);
for (char j=0; j<5; j++) x.StaticVectorOfChars.push_back('a' + j);
std::vector<int>::iterator vii = x.VectorOfInts.begin();
while (vii != x.VectorOfInts.end())
{
std::cout << *vii;
std::cout << " displayed from x.VectorOfInts" << std::endl;
vii++;
}
std::vector<char>::iterator vci = x.StaticVectorOfChars.begin();
while (vci != x.StaticVectorOfChars.end())
{
std::cout << *vci;
std::cout << " displayed from MyClass::StaticVectorOfChars";
std::cout << std::endl;
vci++;
}
std::vector<MyClass> vy;
for (i=0; i=5; i++) vy.push_back(MyClass());
return 1;
}