Introduction
C++ does not have a globally-inherited Object
class, but with the objectify design pattern, you are able to achieve similar results.
Background
As is common in any programming language, the greatest boon usually also turns out to be the greatest bane. In C++, this comes in many forms. One of these is the lack of a globally-inherited object
class, as there is in C#. This is a great advantage in that the overhead associated with a globally-inherited class is not present. It is a bane in that it results in a large degree of duplicated type-dependent code.
Take, for example, an STL list or map. If there were a global Object
class, there would be a single implementation of sorting or red-black trees, which would be contained inside of a lib file. You wouldn't have large, complex algorithms in a .h header file, and you wouldn't have the same large algorithm replicated 10 times over for each type you've associated with the map or list.
Enter the objectify design pattern. It keeps the boon of C++ of not having (nor needing) a common, global Object
class that all other objects, including simple types, inherit from. However, it still allows for type-independent algorithms to be stored away in a CPP file and compiled into a LIB file. No massive inlining in sub-optimal compilers, no compilation of the same method 10 times.
There is, of course, a cost. As would be the case with a global Object
class, type-independent functionality requires virtual functions. There is, of course, an overhead associated with this, usually in the form of an icache miss.
Conversely, because you are using a single common compiled function for all types which use a map or a list, this could also result in less icache misses. This is especially true if you are using maps of different types within a short period of time. The greatest boon, of course, is that your executable's memory footprint will be much smaller. In circumstances where this is a requirement, such as development on low-memory platforms (embedded systems, older game consoles), the boon far outweighs the bane.
Using the Code
The Objectify
base classes are easily used from a class. The templatized class should inherit from a non-templatized base, where most of the functionality will be. In the example below, we have a _list_base
class, with a sort()
function.
class _list_base
{
protected:
Objectify::Comparable& m_comparable;
_list_base (Objectify::Comparable& comparable) : m_comparable(comparable) {}
public:
void sort ();
};
In the sort
function, we would use m_comparable
to safely compare instances of the objects in the list:
m_comparable.Compare(node_data(p), node_data(q))
The templatized class, list<>
would look as follows:
template<typename ListElementType>
class list : protected _list_base
{
public:
list()
: _list_base(Objectify::Typed::Comparable<ListElementType>::s_Comparable)
{
}
}
We now have a type-safe templatized list, with common functionality in a type-independent base class.
The zip file includes a fully-functional list class, with a fully-functional merge sort function. Enjoy!
Points of Interest
The Objectify
method of doing complex algorithms gives a good balance of speed and footprint size. I created a simplified STL library using a similar method (using function pointers instead of Objectify
) in the game Medal of Honor on PlayStation 2. The footprint size was significantly decreased using this method instead of standard STL.
History
- 7/23/2009: First upload of
Objectify
classes