See what's going on with flipcode!




This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

 

  Memory Allocation / DLLs / Reference Counting
  Submitted by



(This applies to Win32 primarily) For starters, it is generally a bad idea to free/delete memory returned from a dll function inside another dll/exe. The opposite, new in an exe, and delete in a dll is also true. This depends on which heap is used, but both will almost certainly fail.

For example:

// dllTest.h
sSomething* createSomething();

// dllTest.cpp sSomething* createSomething() { return new sSomething; }


The client might write in an exe something like:

// exeTest.cpp
sSomething* mySomething = createSomething();
// ...
delete mySomething; // <- problem 


This point is made, more clearly perhaps, in "Programming Applications for MS Windows 4th Ed" by Jeffrey Richter (pg 678). The way to solve this problem, is to include a method to delete any objects that are allocated inside the dll:

  // dllTest.h
  ...
  void deleteSomething(sSomething*);

// dllTest.cpp void deleteSomething(sSomething* obj) { delete obj; }




Now consider the case where you wish to use a simple base object to implement reference counting.

For example:

// CObject.h -- part of myDll.dll
class CObject.h
{
public:
  void addRef() { m_refCount++; }
  void release();

CObject() : m_refCount(1) { ... } };

// CObject.cpp void CObject::release() { if ( (--m_refCount) <= 0) delete this; }


A client might be tempted to create an instance of some derived object, say CTexture, like so:

  // EXEtest.cpp
  CTexture* txr = new CTexture; // <- uhmm?

  // ...
  txr-release(); // <- crash!?! 


Here we have the same basic problem (only reversed). Because the object is created by the exe, but will be released by the dll, it will crash.

Luckily the fix is somewhat simple. Actually, I've seen two solutions. One is to override the 'new' operator for any reference-counted class. The operator is then able to malloc memory from the same heap that the relase() call will free from. This can be done with a simple macro inside each class's .h / .cpp file. However, MFC likes to "#define new DEBUG_NEW" for debug builds, so it is possible that you might have problems there.

The other is to do the same basic thing, but use a static 'createInstance()' method. (Making the constructor/destructors protected to prevent accidental new'ing.)

// Texture.h
class CTexture : public CObject
{
public:
  static void createInstance(CTexture** txr);
protected:
  CTexture();
  ~CTexture();
};

// Texture.cpp void CTexture::createInstance(CTexture** txr) { *txr = new CTexture; }


Using a static createInstance method per class has the 'benefit' of providing a simple class factory method for dynamic object creation:

For example:

// EXETest.cpp
CTexture* txr = NULL;
CObject::createInstance(CLSID_Texture, (CObject**)&CTexture);
// ^ maps CLSID_Texture to the static function pointer
// "CTexture::createInstance(...)", calls it, and returns the
// correct object instance.
...
txr-release(); 


This is a bit more than I intended to write for a tip, but maybe someone will find it useful.

-doug
http://freeside.flipcode.com


The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

 

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.