:
notation in the BinarySearchTree
copy
constructor
BinarySearchTree( const BinarySearchTree *src ) : BinaryTree<T>(src) {}calls the corresponding superclass (
BinaryTree
) copy constructor
directly. The constructor still has a body, but in this particular case
it is empty.
The same notation is used to call a constructor for fields of the
class. For example, the BTNode
constructors could (and
arguably should) be written as
BTNode( T element, BTNode* left_ptr = NULL, BTNode* right_ptr = NULL ) : elem(element), left(left_ptr), right(right_ptr) {} BTNode( const BTNode& src ) : elem(src.elem), left(src.left), right(src.right) {}
elem
field of a tree node contains an
actual value rather than a pointer to a value. That means the
elements are actually copied as they are added to the tree. It can be
argued that this is wasteful, and it would be better to store only a
pointer to the node element in each node instead. Also, the copying
requirement restricts the possible type of the elements to those that
have copy constructors and perhaps even =
operators,
depending on how the code is written. Using pointers also allows for
the element of a node to be NULL, which can be useful in some tree
algorithms. Storing pointers only eliminates the issue
of having to destroy the element values when the tree is destroyed.
And of course there is the issue of the extra memory required by the
copy. Nonetheless, storing the values directly has its own set
of advantages that ultimately wins out. Here is why.
BinarySearchTreeIt is nice to be able to writetree;
tree.insert(17)
, rather
than assign a variable containing 17, pass the address, and make sure
that we don't lose that variable. For simple types, copying that
isn't a problem, as it's no more effort to copy an int
or double
than to copy a pointer. And the space required
may well be less than the space required for a pointer.
string
, which has all the necessary functionality
for copying.
BinarySearchTree<string*> tree
. (That works, but
the comparision operations compare the pointers not the node
elements). To store element pointer, we can define a simple wrapper
structure something like
struct StringPointer { string *ptr; StringPointer( const StringPointer& src ) { ptr = src.ptr; } bool operator==( const StringPointer& src ) { return ptr->equals(*(src.ptr)); } ⋮and then use
BinarySearchTree<StringPointer> tree;
templategets translated toclass MyClass { public: T value; bool equals( const MyClass& src ) const { return (value == src.value); } };
class MyClass_int { public: int value; bool equals( const MyClass_int& src ) const { return (value == src.value); } };when invoked as
MyClass<int>
. For a more general
class, MyClass<SomeClass>
class MyClass_SomeClass { public: int value; bool equals( const MyClass_SomeClass& src ) const { return (value == src.value); } };If
SomeClass
doesn't have an ==
operator
defined, it won't compile: the compiler will report an error something
like
no match for `operator==' in `((const MyClassThere is no direct way in C++ to force the type of a template class parameter to have certain functionality when the template object is defined. You have to rely on compilation failure to enforce template types. We're doing that with the type*)this)->MyClass ::value == src.MyClass ::value'
T
of the elem
field of a BTNode
in several places.
In the insertion and look-up methods, we have to assume that T
has ==
, <
, and >
operators
that work properly.
The binary tree search prototype
templateintroduces a second class parameterT* lookup( const K& key ) const;
K
which refers to the
type of the search key. For the lookup
method, we could
assume the element type T
has a get_key()
method and then compare the key
argument to the result
of the get_key()
method on each node element. The problem,
though, is that simple C++ types have no such method, and to make the
tree classes useful we really should let them work with simple types.
Instead we just to assume that there are ==
and <
(and perhaps >
) operators available
that can directly compare the key type K
with the node
element type T
. That is certainly true if the tree node
elements are simple types, and therefore their own key. If we wanted
to use something more complicated for the node elements, such as a
structure with a key and other data, we have to include appropriate
==
and <
operators for that class.