从DLL中删除指针

时间:2016-01-15 00:24:10

标签: c++ dll memory-leaks

ALL,

我有以下问题:

class Base
{
public:
    Base();
    virtual ~Base();
};

class Derived1 : public Base
{
public:
    Derived1();
    virtual Derived1();
};

class Derived2 : public Base
{
public:
    Derived2();
    virtual Derived2();
};

DLL中定义的基类,它是静态链接的,每个子节点都在自己的DLL中定义,并按需加载。

class Foo
{
public:
    ~Foo();
    void CreateObject();
    void SetPointer(Base *base) { m_p = base; };
private:
    void *m_p;
}

Foo::~Foo()
{
    delete m_p;
    m_p = NULL;
}

extern "C" void CreateObject()
{
     Base *base = NULL;
     Foo *foo = new Foo();
     foo->CreateObject( base );
     foo->SetBase( base );
     delete foo;
}

void Foo::CreateObject(Base *m_p)
{
    if( <cond1> )
        m_p = new Derived1();
    if( <cond2> )
        m_p = new Derived2();
}

运行此代码我得到了内存泄漏。通过调试器运行,我发现Derived1类的析构函数永远不会被调用。

我该如何解决?析构函数是虚拟的,应该被调用。唯一的问题是内存分配发生在DLL内部,但析构函数在主应用程序内部调用。

是否可以修复内存泄漏,或者我将不得不重新考虑设计?

谢谢。

2 个答案:

答案 0 :(得分:0)

在一个DLL中分配并在另一个DLL中取消分配是可以使用的东西,但是很棘手。但是,我认为即使所有代码都在同一个DLL中,您也会看到此问题:

在类Foo中,指向Base的指针存储在void *中。这导致编译器忘记&#34; m_p是一个Base *实例,所以它只是在不调用析构函数的情况下释放内存。

使m_p成为基础*应解决此问题。

答案 1 :(得分:0)

在我看来,你正试图实现一个插件系统,所以这个答案将试图勾勒出你如何构建这样的东西以及一些陷入困境的想法。

C ++和DLL有一些与之相关的有趣挑战。例如,必须特别注意:

  • 在DLL之间传递的对象的分配和释放('new'和'delete')
  • 在DLL之间抛出和捕获异常
  • 跨越DLL边界传递STL对象
  • 功能重载

我在这里最常遇到的方法是仔细定义从DLL导出的函数,然后标记extern“C”,避免重载它们,并且永远不会从这些函数中抛出异常。

虽然MSVC确实支持可导出类,但我建议您避免使用它们,因为您可能会很快遇到上面列出的问题区域。

无论如何,你可以相对安全地依赖的一件事是在DLL之间共享接口类。接口类是仅包含纯虚方法的类。例如:

id

我们将Plugin的声明放在一个头文件中,该头文件可以包含在应用程序中以及插件DLL的实现中。

请注意,还没有任何dllexported。我们只会去dllexport C风格的功能。按照惯例,我们会说我们的插件DLL必须提供“CreatePlugin”功能。这是一个例子:

id

加载dll的应用程序可以这样做:

class Plugin
{
public:
    virtual void DoFoo() = 0;
    virtual void DoBar() = 0;
};

我省略了必要的FreeLibrary调用。更有趣的是我们如何处理释放我们创建的插件。应用程序不一定知道CreatePlugin如何分配插件实例,因此应用程序“删除插件”是不安全的。相反,我们需要告诉插件DLL本身我们已经完成了它。

最明显的方法是在插件中添加“Destroy”方法:

class FirstPlugin : public Plugin
{
public:
    virtual void DoFoo() { std::cout << "FirstPlugin says FOO!\n"; }
    virtual void DoBar() { std::cout << "FirstPlugin says BAR!\n"; }
};

extern "C"
{
    __declspec(dllexport) Plugin* CreatePlugin()
    {
        return new FirstPlugin();
    }
}

可能的实现方式是:

typedef Plugin* (*CreatePluginFn)();
HMODULE module = LoadLibrary("first.dll");

CreatePluginFn createPlugin = (CreatePluginFn)GetProcAddress(module, "CreatePlugin");

Plugin* plugin = createPlugin();
plugin->DoFoo();
plugin->DoBar();

现在调用者做了:

class Plugin
{
public:
    virtual void Destroy() = 0;
    virtual void DoFoo() = 0;
    virtual void DoBar() = 0;
};

我认为这涵盖了基础知识。事实证明,当你构建这样的代码时,有一些常见的模式,例如:

  • 使用引用计数而不是大锤Destroy方法
  • 界面发现(比如问一个插件“你能做Foo吗?”)
  • 在DLL中支持多个“CreatePlugin”样式函数

这些(例如COM)的现有解决方案可能很有意思。