我正在开发一个带有插件系统的程序,该系统允许用户以DLL文件的形式开发自己的模块。 模块应该使用由应用程序的所有组件导入的DLL中定义的对象。以下是示例对象的样子:
#include <boost/system/api_config.hpp>
#if defined BOOST_WINDOWS_API
#ifdef EXPORT
#define API __declspec(dllexport)
#else
#define API __declspec(dllimport)
#endif
#else
#define API
#endif
class A
{
public:
API A();
API virtual ~A();
};
所有DLL都是静态构建的(使用自己的CRT)并使用完全相同的编译标志。我知道通过DLL边界交换对象会变得毛茸茸,所以我几乎到处都使用boost::shared_ptr
s。但是对象构造函数有一个难点:如果我在堆栈上创建对象(来自不同的DLL),一切都按预期工作。但是如果我使用运算符new
,那么删除对象时堆就会被破坏。
A a; // Works fine, no problem when the object goes out of scope.
A* b = new A();
delete b; // Causes heap corruption!
解决这个问题的正确方法是什么?如果我必须在对象的DLL中定义一个方法,例如A* A::create() { return new A(); }
,我觉得代码的可读性会降低。在最糟糕的情况下,我考虑让new
运营商保密,以确保用户不会使用它。
答案 0 :(得分:2)
根据您对我的评论的回复:/MT
和/MTd
不要
适用于DLL(即使它们是默认值)。如果你想
要使用DLL和动态分配,必须使用/MD
或
/MDd
。当您使用/MT
或/MTd
时,您可以有效地告诉我们
系统为每个DLL使用单独的堆。意思就是
在一个中分配,在另一个中删除会破坏
堆。当析构函数是虚拟的时,实际的delete
将在delete
表达式中的析构函数 not 中。
(实际问题是malloc
和free
,由...调用
operator new()
和operator delete()
函数。)
解决这个问题的经典方法是使用工厂
动态分配的方法和静态或成员函数
删除。另一种选择(未尝试,但我认为会这样
(工作)是定义非内联operator new()
和operator
delete()
成员,这些成员只转发到malloc
和free
。
(当然是operator new
,你必须检查
你从malloc
获得的指针不为空,并抛出一个
std::bad_alloc
如果是的话。)
但这些只是针对不应该出现的情况的解决办法
存在。使用/MD
或/MDd
一切正常,就是这样
你应该使用什么(即使这意味着你可以合法地使用
在没有许可证的计算机上部署调试版本
对于Visual Studios)。
答案 1 :(得分:1)
我认为这个问题可能与你混合堆的事实有关。当您使用静态链接的所有CRT构建DLL时,意味着它维护自己的堆。但是,主机进程中的命令new
和delete
正在使用进程堆。我认为这种configuratin可能会导致问题。我认为最好的方法是在DLL中添加两个方法:CreateA()
和DestroyA()
,并仅使用它们来分配/销毁DLL A中的堆对象。
答案 2 :(得分:0)
解决方案是永远不会从DLL导出构造函数。改为导出工厂功能。在您进行混凝土课程时,切勿出口。仅导出纯抽象类。无论如何,这是一种很好的风格,因为减少了耦合和所有相关的好东西。