13.6.2.1. Tree Example 1: One Template Variable

The Tree template example program consists of three files:

  1. Tree.h: demonstrates how to create a general data structure based on templates (notice that the template class and all functions must be in a header file)
  2. Employe.h: a simple class instances of which are stored in the Tree
  3. Employee.cpp: simple driver that defines and uses a Tree to store instances of Employee - demonstrates how to create an instance of a template class
#include <iostream>
using namespace std;


template <class T>
class Tree
{
	private:
		T	data;
		Tree*	left = nullptr;				// 1
		Tree*	right = nullptr;

	public:
		//Tree() : left(nullptr), right(nullptr) {}	// 2
		~Tree();
		T* insert(T& data);
		T* search(T& data);
		void list()  { right->_list(); } 		// 3

	private:
		void	_list();				// 4
};
Tree.h: class specification.
  1. Support for initializing member variables in the class specification was added with the ANSI 2014 language standard
  2. Prior to the 2014 standard, member variables were only initialized by the constructor
  3. The public interface for the list operation calls the private _list function, initiating the descent on the right sub-tree. The simple list function contributes to a "clean" public interface, allowing the class designer to implement _list as a recursive function.
  4. The private _list function does most of the work of the list operation.
template <class T>
Tree<T>::~Tree()
{
	if (left != nullptr)
		delete left;
	if (right != nullptr)
		delete right;
}
Tree.h: destructor. Although perhaps not immediately obvious, the destructor is a recursive function: delete left and delete right each trigger a recursive call to the destructor function.
template <class T>
T* Tree<T>::insert(T& data)
{
	Tree* root = this;					// 1
	Tree* down = right;

	while (down != nullptr && !(down->data == data))	// 2
	{
		root = down;
		if (data < down->data)
			down = down->left;
		else
			down = down->right;
	}

	if (down != nullptr)					// 3
		return &down->data;

	down = new Tree;					// 4
	down->data = data;
	if (data < root->data)					// 5
		root->left = down;
	else
		root->right = down;

	return &down->data;					// 6
}
Tree.h: insert function. There are many ways to implement the insert operation; this implementation has three important characteristics: This implementation requires that operator< and operator== are implemented for the class that replaces the template variable T.
  1. root and down are used to locate the insertion point
  2. Loops until (a) the bottom of a sub-tree is located or (b) a previously inserted copy of the data is found. It is more common to implement operator== than it is is to implement operator!=, so the equality operator is used here with the negation operator ( ! )
  3. Returns a pointer to the previously inserted data if present in the tree
  4. Creates a new tree node and installs the data if the data is not present in the tree
  5. Binary trees are ordered. A typical ordering is to insert new data in the left sub-tree if it is less than the data in the root of the current sub-tree; other wise it is inserted in the right sub-tree
  6. Returns a pointer to the newly inserted data
template <class T>
T* Tree<T>::search(T& data)
{
	Tree* root = right;					// 1

	while (root != nullptr && !(root->data == data))	// 2
	{
		if (data < root->data)
			root = root->left;
		else
			root = root->right;
	}

	if (root == nullptr)					// 3
		return nullptr;
	else
		return &root->data;
}
Tree.h: search function. The search operation returns a pointer to the data if it is found, otherwise it returns nullptr.
  1. root is used to descend the tree while looking for the tree node that stores the searched for data
  2. Loops until (a) the bottom of the tree is located or (b) the searched for data is located.
  3. Returns a pointer to located data or nullptr if the data is not found.
template <class T>
void Tree<T>::_list()
{
	if (left != nullptr)
		left->_list();
	cout << data << endl;
	if (right != nullptr)
		right->_list();
}
Tree.h: private _list function. The _list function recursively descends the tree printing the stored data in-order (pre-order and post-order are achieved by moving the cout statement to the top or bottom of the function respectively). The _list function assumes that operator<< is overloaded for the template class that replaces T.
#include <iostream>
#include <string>
using namespace std;

class Employee
{
	private:
		string	name;
		int	id;

	public:
		Employee(string n = "", int en = 0) : name(n), id(en) {}

		bool operator==(Employee& e) { return name == e.name; }		// 1
		bool operator<(Employee& e) { return name < e.name; }		// 2

		friend ostream& operator<<(ostream& out, Employee& me)		// 3
		{
			out << me.name << " " << me.id;
			return out;
		}
};
Employee.h: Employee class specification.
  1. Required for Tree.insert and Tree.search
  2. Required for Tree.insert and Tree.search
  3. Required for Tree.list
#include <iostream>
#include <string>
#include "Tree.h"
#include "Employee.h"
using namespace std;

int main()
{
	Tree<Employee>	tree;
	string		name;

	while (true)
	{
		cout << "N\tEnter a new Employee" << endl;
		cout << "S\tSearch for an Employee" << endl;
		cout << "L\tList all Employees" << endl;
		cout << "E\tExit" << endl;

		cout << "Operation: ";
		char operation;
		cin >> operation;
		cin.ignore();

		switch (operation)
		{
			case 'N'	// create a new Employee and insert it into the Tree
			case 'n':
			{
				cout << "Employee name: ";
				getline(cin, name);
				int	number;
				cout << "Employee Number: ";
				cin >> number;
				cin.ignore();
				Employee employee(name, number);		// Data
				tree.insert(employee);
				break;
			}

			case 'S':	// search for an exiting Employee in the Tree
			case 's':
			{
				cout << "Employee name: ";
				getline(cin, name);
				Employee employee(name);
				Employee* e = tree.search(employee);
				if (e != nullptr)
					cout << "Employee: " << *e << endl;
				else
					cout << name << " NOT FOUND" << endl;
				break;
			}

			case 'L':	// list all of the Employees in the Tree
			case 'l':
				tree.list();
				break;

			case 'E':	// exit the program
			case 'e':
				return 0;
				//exit(0);	// ends the program but doesn't call the destructor

			default:
				cerr << "Unrecognized operation: \"" << operation << "\"" << endl;
				break;
		}
	}

	return 0;
}
Employee.cpp. A client program that uses the Tree template class. The highlighted statement demonstrates how to define an instance of the Tree class and to specify the class type (Employee) stored in the Tree. The class name "Employee" replaces the template variable T when the program is compiled.

Downloadable Code