2014-06-18 54 views
1

我正在开发一个带有插件系统的程序,它允许用户以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。围绕对象构造函数有一个困难:如果我在堆栈上创建对象(来自不同的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

我不知道我得到您的问题。从你展示的内容来看,你的DLL里没有任何东西,因为所有的函数都是“inline”的。你通常不需要在内联函数或模板上使用'__declspec'。 –

+0

如果我更改代码,以便构造函数和析构函数不是内联的,它对我来说很有用。您确定您为DLL和使用它的代码都指定了兼容的链接器选项吗? (特别是,你可能需要系统地使用'/ MD'或'/ MDd',没有它,你会看到这样的问题。) –

+0

我的构造函数/析构函数实际上不是内联函数,我编辑了文章以反映它。我确认我得到了堆腐败。我的构建标志确实是'/ MT'和'/ MTD',但我不想改变它们。 – executifs

回答

2

根据您responsed到我的评论:/MT/MTd不要” t 可以很好地处理DLL(即使它们是默认设置)。如果你想 使用DLL和动态分配,你必须使用/MD/MDd。当您使用/MT/MTd时,您可以有效地告知 系统为每个DLL使用单独的堆。这意味着 分配在一个,并在另一个删除将损坏 堆。并且当析构函数是虚拟的时,实际的delete 将在delete表达式中的析构函数中,而不是。 (实际的问题是与mallocfree,由 operator new()operator delete()函数调用。)

解决此工作的传统方法是使用工厂 方法用于动态分配和静态或成员函数 删除。一个替代方案(未尝试,但我认为它将 工作)是定义非内联operator new()operator delete()成员,它们只是转发到mallocfree。 (在operator new的情况下,当然,你必须检查 您从malloc得到的指针不为空,并抛出一个 std::bad_alloc如果它是。)

但这些都只是变通的 不存在的情况。一切工作正常与/MD/MDd,这是 你应该使用什么(即使这意味着你不能合法地 在没有许可证 视频工作室的机器上调试版本)。

1

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

0

解决方法是从不从DLL中导出构造函数。改为导出工厂功能。从不输出具体课程。仅导出纯粹的抽象类。无论如何,这是一个很好的风格,减少耦合和所有相关的好东西。