The Artima Developer Community
Sponsored Link

Heron-Centric: Ruminations of a Language Designer
Working Around Non-Virtual Destructors
by Christopher Diggins
May 2, 2005
Summary
It is commonly recommended in C++ to publicly inherit from classes which have virtual destructors, to avoid possible memory leaks. Here I present a pointer class which allows us to polymorphically use a base class without requiring a virtual destructor.

Advertisement

When inheriting from a class which we intend to use polymorphically in C++, it is required that this class has a virtual destructor. Without this, we run into a problem where the derived constructor will not get called. [See http://www.gotw.ca/publications/mill18.htm]

For example consider the following code:

#include <iostream>
using namespace std;

class MyBaseClass {
public:
  MyBaseClass() {
  }
  ~MyBaseClass() {
  }
};

class MyDerivedClass : public MyBaseClass {
public:
  MyDerivedClass() : MyBaseClass() {
    m = new int;
  }
  ~MyDerivedClass() {
    cout << "deleting m" << endl;
    delete m;
  }
private:
  int* m;
};

int main(int argc, char* argv[])
{
  MyBaseClass* p = new MyDerivedClass;
  delete p; // ~MyDerivedClass never called, and m is never deleted :-(
  system("PAUSE");
  return 0;
}
This is a rather nasty and hard to detect problem. The most commonly prescribed solution is to simply declare ~MyBaseClass() virtual. This however introduces a virtual dispatch table into MyBaseClass, which may not be desirable, as it potentially increases the size of the object, and it hurts performance. An alternative solution (which may be completely novel, I don't know) is to use the following specialized pointer class:
  template<typename target_type>
  class base_class_ptr {
   public:
      // forward declarations
      template <class T>
      struct functions;
      template <class T>
      base_class_ptr(T* x)
        : m_a(x), m_t(&functions<T>::table)
      {}
      target_type* operator->() {
        return static_cast<target_type*>(m_a);
      }
      void Delete() {
        m_t->Delete(m_a);
        m_a = NULL;
      }
      // Function table type
      struct table {
        void (*Delete)(void*);
      };
      // For a given referenced type T, generates functions for the
      // function table and a static instance of the table.
      template<class T>
      struct functions
      {
        static typename base_class_ptr<target_type>::table table;
        static void Delete(void* p) {
            delete static_cast<T*>(p);
        }
     };
   private:
      void* m_a;
      table* m_t;
  };

  template<typename target_T>
  template<class T>
  typename base_class_ptr<target_T>::table
  base_class_ptr<target_T>::functions<T>::table = {
      &base_class_ptr<target_T>::template functions<T>::Delete
  };
The example before can now be rewritten as:
int main(int argc, char* argv[])
{
  base_class_ptr<MyBaseClass>* p(new MyDerivedClass)
  p.Delete(); // ~MyDerivedClass now called, and m is deleted :-)
  system("PAUSE");
  return 0;
}
Even though the code is complicated what is happening is simply that base_class_ptr carries around a pointer to the appropriate Delete function which gets generated by the compiler. In effect the trade-off is that instead of increasing the size of the object, we increase the size of the pointer.

At this point, I am unaware of any other class which accomplishes the same thing. If anyone has any similar prior work they can point me to it would be greatly appreciated.

Talk Back!

Have an opinion? Readers have already posted 11 comments about this weblog entry. Why not add yours?

RSS Feed

If you'd like to be notified whenever Christopher Diggins adds a new entry to his weblog, subscribe to his RSS feed.

About the Blogger

Christopher Diggins is a software developer and freelance writer. Christopher loves programming, but is eternally frustrated by the shortcomings of modern programming languages. As would any reasonable person in his shoes, he decided to quit his day job to write his own ( www.heron-language.com ). Christopher is the co-author of the C++ Cookbook from O'Reilly. Christopher can be reached through his home page at www.cdiggins.com.

This weblog entry is Copyright © 2005 Christopher Diggins. All rights reserved.

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use