views:

254

answers:

2

Sorry if this was asked already, but I had a hard time searching for destructor and access violation =)

Here's C++ pseudo-code the scenario:


In DLL1 (compiled with /MT)

class A
{
public:
    virtual ~A()      <== if "virtual" is removed, everthing works OK
    {
    }
}

class B : public A
{
public:
    __declspec( dllexport ) ~B()  // i did try without exporting the destructor as well
     {
     }      <== Access Violation as it returns (if fails in assembly at delete operator...)
}

In DLL2 which links to DLL1

main()enter code here
{
    B* b = new B();
    delete b;           <== Access Violation
}

What is going on? Am I having a brain shart? If I make A's destructor non-virtual, everything works OK - even A and B's destructor get called (as if A's destructor was virtual - is this due to the fact that it's public?).

My main question though - is why is there an access violation when the base classe's destructor is declared as virtual?

A: 

Because it's crashing in the delete operator, and because you said you're compiling with /MT, I believe that the cause is that your two DLLs don't share the same heap: because they're each linking to a static library, they're each getting their own private copy of the run-time heap; and you're effectively allocating memory in one DLL from one heap, and deleting the memory in another DLL from another heap.

To work-around this, you can declare that the destructor is protected, and don't export it. Instead, create a static destroy function:

class A
{
public:
  __declspec( dllexport ) static void destroy(A* self) { delete self; }
protected:
  virtual ~A() {}
};

int main()
{
  B* b = new B();
  A::destroy(b); //instead of delete b
  return 0;
}

Alternatively, and you may prefer this because it doesn't involve changing the source code, ensure that both DLLs are build to use the same heap, i.e. using the DLL versions of the C-runtime, which I think means using the /MD option instead of /MT.

ChrisW
"you're effectively allocating memory in one DLL from one heap, and deleting the memory in another DLL from another heap."I don't think that is what the code looks like.
leiz
Although that's not what the code looks like, nevertheless I think that's what's happening. It might be that whether or not the destructor is virtual affects whether the destructor is inlined into the DLL which invokes it.
ChrisW
Wouldnt having the destructors in the cpp rather than inline in the header also fix this?
0xC0DEFACE
No I think it would make it worse (similar to declaring it virtual): because it's being created/allocated in the other DLL. Anyway the correct fix is probably to use a single shared heap instance (`/MD`) so that it doesn't matter which DLLs create and/or destroy any object.
ChrisW
+1  A: 

Thanks guys! Thanks ChrisW.. looks like that's exactly what's happening. Only other thing I needed to add was a static allocator (static createNew()):

class A
{
public:
  __declspec( dllexport ) static void destroy(A* self) { delete self; }
protected:
  virtual ~A() {}
};

class B : public A
{
 protected: 
   B();
 public:
   __declspec( dllexport ) static B* createNew() { return new B(); }
}

int main()
{
  B* b = B::createNew()
  A::destroy(b); //instead of delete b
  return 0;
}

(btw compiling with /MD is not an option for me given my deployment environment)

Sheado