文章目录
- 问题
- 原则
- 示例一
- 解决方案
- 示例二
- 解决方法
- 参考
问题
在 windows 平台下,如果在动态库的接口中使用 std::string 或其它 std 容器,会导致崩溃或其它内存问题,所以一般要求动态库的接口必须是 C 语言实现。
原则
一个原则:某个模块中分配的空间就应该由它来释放!比如说在 dll 中分配的空间就应由这个 dll 来释放,而不应该由 main 来释放,因为 dll 中用来分配空间的环境可能和 main 中用来分配的环境不一样。关于这一点 window核心编程 中的 dll 里面进行了讲解,但是说真的我还是不明白为什么会这样。但是要记住这个原则!
示例一
main 中的如下语句:
string str1("l10");
string str2("l10value");
pi->addElement(str1, str2 );
//pi是指向 dll 提供的一个接口的指针。
dll 中是如下实现 addElement 函数的:
bool Test::addElement(string elementName, string elementValue)
{
// ... 省略
return true;
// 返回时对 elementName,elementValue 进行析构,这导致释放它们具体字符串的空间,
// 但是这些字符串的空间是在 main 中分配的,所以出现运行时错误!!!(其实是无效内存访问)
}
解决方案
对于上面这种情况我们只要把 dll 的改成引用就可以了:
bool Test::addElement(string &elementName, string &elementValue)
{
// ... 省略
return true;
}
备注:感觉把 string 用于 dll 并不是一个好主意。
示例二
前言:为什么要用浅拷贝。因为假如字符串空间很大的话,若不用浅拷贝则将非常费时且浪费空间。
-
string 作为 dll 导出接口的方法的输入参数,这时可以作为引用来传递。这种情况下容易解决,如上。 这里是指 dll 中不会对 string 作任何改变。
-
dll 导出接口的方法返回一个 string,也就是字符串空间在 dll 中分配,然后在 main 中获得这个 string, 则以为在 main 中释放空间,但由于是浅拷贝,所以将出现错误。
dll 中的代码如下:
string Test::getString()
{
string s("abc");
return s;
}
main 中的代码如下:
main()
{
// 省略 ...
string strretdll = pi->getString();
return 0;
// 返回时调用 string 的析构函数,进而释放字符串空间,但由于这个空间不是在 main 模块
// 中分配的,这将导致错误。
}
好问题出来了:在 getString 返回时为什么不会把字符串空间析构掉呢? 事实上 string 的析构函数要调用一个称为 _Tidy(bool) 的函数来处理。注意不同的 stl 实现如何析构 string 的具体方式是不一样的。总之经过我观察之后,在 getString 返回时并不释放字符串空间,尽管执行了析构函数。我想这一点有点像智能指针。
解决方法
对于这种情况的解决方法:
-
把空间分配和释放均在 main 中,但是 main 并不知道要具体分配多少空间
-
把空间分配和释放均在 dll 中, 但是如何才能在 main 中调用 dll 的方法来要求 dll 释放空间。
// 现在该想到 com 中 IUnknown 的重要作用了吧!!!! -
string 作为 dll 导出接口的方法的输出参数。这种情况同样出现情况 2 的问题。
-
string 作为 dll 导出接口的方法的输入输出参数。具有输出特性时和情况 2 相似。
思考:能不能用指向 string 的指针呢?
不方便!!
最后我下一个结论:在 dll 中 string 不能作为输出属性的参数!!
所以,我们只能显式地在 dll 中定义一个输出函数,用这个输出函数来释放 dll 分配的空间!!
不过也可以在 VC 工程中使用 PROGECT—>SETTINGS 中,选择 C/C++ CATEGORY 选择 code generation 中 user run-time lib 选择 debug multithreanded ,这样也可以避免 string 内存没有释放问题。建议一般不在动态链接库中返回 string 。
参考
DLL string
C++实现简单的string
☆