Igor
Igor

Reputation: 6285

Deleting pointer from DLL

ALL,

I have a following issue:

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

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

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

The base class defined in the DLL, which is statically linked and each child is defined in its own DLL which is loaded on demand.

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();
}

Running this code I am getting memory leak. Running thru the debugger, I see that destructor for Derived1 class is never called.

How do I possibly fix it? The destructors are virtuals and should be called. The only issue is that memory allocation is happening inside a DLL but destructor is called inside the main application.

Is it possible to fix a memory leak or I will have to rethink the design?

Thank you.

Upvotes: 0

Views: 1421

Answers (2)

Damyan
Damyan

Reputation: 1613

It looks to me like you're trying to implement a plugin system so this answer is going to try and sketch out how you'd build such a thing and some of the pitfalls that come to mind.

C++ and DLLs have some interesting challenges associated with them. For example, extreme care must be taken with:

  • allocation and deallocation ('new' and 'delete') of objects passed between DLLs
  • throwing and catching exceptions between DLLs
  • passing STL objects across DLL boundaries
  • function overloading

The approach I've most experience with here is to carefully define the functions exported from a DLL, mark then extern "C", avoid overloading them, and to never throw exceptions from these functions.

Although MSVC does support exportable classes, I'd recommend avoiding them since you'll likely quickly run into the problem areas listed above.

Anyway, one thing that you can relatively safely rely on is sharing interface classes between DLLs. Interface classes are classes that only contain pure virtual methods. So for example:

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

We put Plugin's declaration in a header file that can be included by the application as well as the implementation of the plugin DLLs.

Note that nothing has been dllexported yet. We're only going to dllexport C-style functions. By convention, we'll say that our plugin DLLs must provide a "CreatePlugin" function. Here's an example one:

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();
    }
}

And the application that loads the dll can do this:

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

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

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

I've omitted the necessary FreeLibrary call. More interesting though is how we deal with freeing up the plugin we created. The application doesn't necessarily know how CreatePlugin allocated the Plugin instance, so it isn't safe for the application to "delete plugin". Instead, we need to tell the plugin DLL itself that we're done with it.

The most obvious way to do this would be to add a "Destroy" method to Plugin:

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

And a possible implementation of this would be:

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

So now the caller does:

plugin->Destroy();
plugin = NULL; // we mustn't use plugin after we're destroyed it!

I think that this covers the basics. It turns out that when you build a lot of code like this that there are common patterns such as:

  • using reference counting rather than a big-hammer Destroy method
  • interface discovery (like asking a plugin "can you do Foo too?")
  • supporting many multiple "CreatePlugin" style functions in a DLL

There are existing solutions to these (eg COM) that may be interesting to look at.

Upvotes: 1

Damyan
Damyan

Reputation: 1613

Allocating in one DLL and deallocating in another is something that can be made to work, but is tricky. However, I think that you'd see this issue even if all the code was in the same DLL:

In class Foo, the pointer to Base is stored in a void*. This has the result that the compiler "forgets" that m_p is a Base* instance, and so it just frees the memory without calling the destructor.

Making m_p a Base* should resolve this.

Upvotes: 0

Related Questions