C++ Trees Part 1
2. std::map<> versus core::tree<>Many experienced C++ programmers use C++'s standard template library's (STL) map<> and multimap<> to generate trees. Yet, these containers make poor substitutes for true tree designs for a variety of reasons. This section will take a closer look at the pitfalls of using C++'s std::map<> and std::multimap<> for tree containment. 2.1 Basic std::map<> tree implementationBefore beginning the implementation of a tree using std::map<>, a framework must be defined in which the tree will work. For the purposes of this article the following namespace will be used to control the number of leaves generated at the root level and maximum branch depth of the trees constructed:
namespace nTreeData
{
const int kMaxLeaves = 10;
const int kMaxDepth = 5;
}
Additionally, the following class defined (in figure 2.2) will be used as the leaf node, which will be inserted at every branch of the tree.
class Leaf
{
public:
Leaf() : value_(0) {}
explicit Leaf(const int &value) : value_(value) {}
const int &value() const { return value_; }
bool operator==(const Leaf &rhs) const
{ return this->value() == rhs.value(); }
bool operator<(const Leaf &rhs) const
{ return this->value() < rhs.value(); }
private:
int value_;
};
Combining the definitions provided in figure 2.1 and figure 2.2, an example of an std::map<> tree can be constructed:
#include <map>
#include <iostream>
typedef std::map<Leaf, int> LeafMapConcrete;
typedef std::map<Leaf, int>* LeafMapPointer;
typedef std::map<Leaf, LeafMapPointer > LeafMap;
void fun()
{
using namespace nTreeData;
LeafMap leafTree;
////////////////////////////////////////////////////
// create a simple leaf tree
////////////////////////////////////////////////////
for (int i = 0; i < kMaxLeaves; ++i)
{
// insert a starter leaf
LeafMapPointer p = new LeafMapConcrete;
leafTree.insert(LeafMap::value_type(Leaf(i), p));
LeafMap::iterator iter = leafTree.find(Leaf(i));
// continue inserting children inside of children
for (int depth = 0; depth < kMaxDepth; ++depth)
{
LeafMapPointer inner = new LeafMapConcrete;
LeafMap* outer = (LeafMap*)(iter->second);
outer->insert(LeafMap::value_type(Leaf(depth), inner));
iter = outer->find(Leaf(depth));
}
}
////////////////////////////////////////////////////
// deallocate the leaf tree
////////////////////////////////////////////////////
for (LeafMap::iterator destroy = leafTree.begin();
destroy != leafTree.end();
++destroy)
{
LeafMap::const_iterator inner = destroy;
LeafMap* iterMap = (LeafMap*)(destroy->second);
LeafMap* lastMap;
for (inner = iterMap->begin(); inner != iterMap->end();
inner = iterMap->begin())
{
lastMap = iterMap;
// move the iterMap forward
iterMap = (LeafMap*)inner->second;
delete lastMap;
}
}
}
Figure 2.3 demonstrates how to use a std::map<> to implement a tree in C++. The above implementation is a common method for overcoming STL's lack of native tree container support. Unfortunately, it has many problems:
Consider now the same tree being generated using the core::tree<> container:
#include "tree.h"
#include <iostream>
void fun()
{
using namespace nTreeData;
using namespace core;
core::tree<Leaf> leafTree;
////////////////////////////////////////////////////
// create a simple leaf tree
////////////////////////////////////////////////////
for (int i = 0; i < kMaxLeaves; ++i)
{
// insert a starter leaf
tree<Leaf>::iterator iter = leafTree.insert(Leaf(i));
// continue inserting children each time
for (int depth = 0; depth < kMaxDepth; ++depth)
{
// insert and step into the newly created branch
iter = iter.insert(Leaf(depth));
}
}
}
The code within figure 2.4 implements the same tree as the std::map<> solution within figure 2.3. However, the tree constructed using the core::tree<> container (figure 2.4) is less complex than the tree constructed using std::map<>. Furthermore, the core::tree<> implementation requires less code than the std::map<> implementation. Lastly, the core::tree<> implementation has none of the pitfalls the std::map<> implementation has:
Reviewing the above implementations of trees, it is clear the tree implementation using std::map<> is error-prone, complex and run time unsafe. However, the tree implementation using the core::tree<> is simple and elegant, and solves the tree design problem with no additional programmatic overhead. The core::tree<> code is also easier to understand than the std::map<> code which leads to easier code maintenance. |
|