4

Say I have the following in a DLL implementation (eg, it would have a cpp file):

class Base
{
protected:
    Something *some;
public:
    virtual void init()
    {
        some = new Something();
    }

    virtual  ~Base()
    {
        delete some;
    }

};

Then in my exe I make:

class Derived : public Base
{
public:
    virtual void init()
    {
        some = new SomethingElse();
    }
};

int main()
{
   Base *blah = new Derived;
   delete blah;
}

Would this ever cause problems if the DLL is ran with a different runtime than the exe?

if so, is there a non boost, non c++ 0x solution

Thanks

Zooba
  • 11,221
  • 3
  • 37
  • 40
jmasterx
  • 52,639
  • 96
  • 311
  • 557
  • 1
    @Milo:Why are you not overriding the destructor as well to delete the `SomethingElse`? – Cratylus Jan 10 '11 at 07:17
  • 2
    @user384706 I thought the base destructor would take care of it? – jmasterx Jan 10 '11 at 07:19
  • 1
    @user384706 The base class destructor deletes `some` and is properly declared virtual, so he doesn't need to. – Dawson Jan 10 '11 at 07:20
  • 1
    What's "different runtime than the exe"? exe and dll compiled with different compilers? – Shelwien Jan 10 '11 at 07:20
  • @Shelwien - in windows you can compile the c runtime directly into the various libraries you are linking to. This then leads to the problem of each DLL having its own heap manager. Windows uses the heap to implement the free store. Thus an element created on the free store in one part of your program MUST be deleted by that same part of your program (exe vs. dlls and such) or else you'll be deleting something that your heap manager never created and BOOM!!! It's a major pain in the butt that causes endless pain and suffering to those who do it. – Edward Strange Jan 10 '11 at 07:30
  • @Noah Roberts: A DLL only has its own heap if it is statically linked with the runtime. If all DLL and the application are dynamically linked with the runtime (which is the standard behavior for the last 10 years) then they will have the same heap. – Martin York Jan 10 '11 at 08:19
  • @Martin: also if one is a release and the other a debug build? – stijn Jan 10 '11 at 09:23
  • @Noah Roberts: "Windows uses the heap to implement the free store." is a rather confusing sentence. _Windows_ heaps are unrelated to the C++ free store. Those Windows heaps are shared across the entire process, EXE and DLLs combined. DLL's may (depending on the C++ compiler and its settings) have isolated C++ free stores, though. – MSalters Jan 10 '11 at 10:23
  • @Martin - that's exactly what I said. – Edward Strange Jan 10 '11 at 17:32
  • @stijn: You can not safely link together debug and release bits. It must be all one or the other. There are just so many ways that goes wrong. This is why the MS IDE builds all debug and release things into separate directory structures so that they can not be confused. – Martin York Jan 10 '11 at 19:09

5 Answers5

3

I think you need to write ~Derive() like this

~Derived()
{
   delete some;
   some = 0; //this is must; so that `delete some` in ~Base() works perfectly; 
  //note `delete (void*)0` is fine in C++!
}

Explanation :

Why you need to write this even though the ~Base() does the same thing (it looks it does the same thing) is because ~Derived() ensures that you delete your object from the same heap/memory-pool/etc they were created on.

See these topics:

How to use a class in DLL?
Memory Management with returning char* function


EDIT:

Better would be to add one more virtual function, say deinit(), (a counter-part of your virtual void init()) , redefine this too when you redefine init(), and do the de-allocation there in deinit().

//DLL
class Base
{
protected:
    Something *some;
public:
    virtual void init()
    {
        some = new Something();
    }
    virtual void deinit()
    {
        delete some;
    }
    virtual  ~Base() { deinit(); }
};

//EXE
class Derived : public Base
{
public:
    virtual void init()
    {
        some = new SomethingElse();
    }
    virtual void deinit()
    {
        delete some; //some=0 is not needed anymore!
    }
};
Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • But if I do not what can happen? – jmasterx Jan 10 '11 at 07:25
  • 1
    Deleting a null (0) pointer is ignored - deleting a valid pointer twice may cause crashes, depending on how brittle your operating system is. – Zooba Jan 10 '11 at 07:26
  • 2
    Under normal situations the DLL will use the same heap as the application. The only time you will have a seprate heap is you explicitly create your own heap or statically link against the runtime (which is specifically advised against by MS). This advice was correct about 10 years ago when statically linking the DLL to the runtime was the standard procedure. – Martin York Jan 10 '11 at 08:16
0

Derived allocated some, thus it maintains the responsibility to deallocate it. Overwrite the destructor...

If you always follow that principle, then your memory handling is much simpler.

Cine
  • 4,255
  • 26
  • 46
  • If he deletes `some` in the destructor for `Derived`, the `Base` destructor will be called immediately after and attempt to delete `some` again, yielding undefined behavior (unless you take the precaution Nawaz did and set it to `NULL` after deletion). – Dawson Jan 10 '11 at 07:28
0

There won't be any problems, though obviously the code contained in Derived won't ever make it across to the other executable.

The destructor for Base will correctly delete some provided Something derives from SomethingElse and has a virtual destructor.

Zooba
  • 11,221
  • 3
  • 37
  • 40
  • Why is it certain that one C++ library's implementation of `delete` gracefully frees memory allocated by another's `new`? – user168715 Jan 10 '11 at 07:39
  • Good luck linking to a C++ DLL from a different compiler - the name mangling will destroy you, which is why `extern "C"` is used - and if you've got the same compiler there's no reason or excuse for different allocators. If you still manage to achieve it, yeah, it's possible it will break. The question didn't quite make clear that that's the important part... – Zooba Jan 11 '11 at 08:49
0

I think you should initialize something in its constructor.

Otherwise the destructor will delete a random piece of memory.

Base::Base()
    : Something(NULL)
{ } 
Martin York
  • 257,169
  • 86
  • 333
  • 562
0

It's ugly code because of the protected variable.

I propose we reshape it up. First, let's make sure that all the ownership logic is insulated, so that it is easier to prove right.

class Base
{
public:
  Base(): some(0) {} // better initialize it...
  virtual ~Base() { delete some; }

  void init() { delete some; some = this->initImpl(); }

private:
  Base(Base const&); // no copy
  Base& operator=(Base const&); // no assignment

  virtual SomeType* initImpl() { return new SomeType(); }

  SomeType* some;
}; // class Base


class Derived: public Base
{
  virtual SomeOtherType* initImpl() { return new SomeOtherType(); }
};

It's only a first step though, because you should not try to manipulate resources directly, you're only going to leak them. So now, we take our shiny interface and reshape the implementation:

// something that you should definitely have for your base classes
class noncopyable { protected: noncopyable() {} private: noncopyable(noncopyable const&); noncopyable& operator=(noncopyable const&); };

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

  void init() { some.reset(this->initImpl()); }

private:
  virtual SomeType* initImpl() { return new SomeType(); }

  std::auto_ptr<SomeType> some;
}; // class Base

// same Derived class, that's the beauty of insulation :)

Isn't it much better ?

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722