导出构造函数的DLL可能会导致堆损坏

时间:2014-06-18 10:13:57

标签: c++ dll

我正在开发一个带有插件系统的程序,该系统允许用户以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运营商保密,以确保用户不会使用它。

3 个答案:

答案 0 :(得分:2)

根据您对我的评论的回复:/MT/MTd不要 适用于DLL(即使它们是默认值)。如果你想 要使用DLL和动态分配,必须使用/MD/MDd。当您使用/MT/MTd时,您可以有效地告诉我们 系统为每个DLL使用单独的堆。意思就是 在一个中分配,在另一个中删除会破坏 堆。当析构函数是虚拟的时,实际的delete 将在delete表达式中的析构函数 not 中。 (实际问题是mallocfree,由...调用 operator new()operator delete()函数。)

解决这个问题的经典方法是使用工厂 动态分配的方法和静态或成员函数 删除。另一种选择(未尝试,但我认为会这样 (工作)是定义非内联operator new()operator delete()成员,这些成员只转发到mallocfree。 (当然是operator new,你必须检查 你从malloc获得的指针不为空,并抛出一个 std::bad_alloc如果是的话。)

但这些只是针对不应该出现的情况的解决办法 存在。使用/MD/MDd一切正常,就是这样 你应该使用什么(即使这意味着你可以合法地使用 在没有许可证的计算机上部署调试版本 对于Visual Studios)。

答案 1 :(得分:1)

我认为这个问题可能与你混合堆的事实有关。当您使用静态链接的所有CRT构建DLL时,意味着它维护自己的堆。但是,主机进程中的命令newdelete正在使用进程堆。我认为这种configuratin可能会导致问题。我认为最好的方法是在DLL中添加两个方法:CreateA()DestroyA(),并仅使用它们来分配/销毁DLL A中的堆对象。

答案 2 :(得分:0)

解决方案是永远不会从DLL导出构造函数。改为导出工厂功能。在您进行混凝土课程时,切勿出口。仅导出纯抽象类。无论如何,这是一种很好的风格,因为减少了耦合和所有相关的好东西。