This example program is similar to the previous example but demonstrates a container class based on two template variables. This version of the tree tree class, named KVTree implements a map container that associates a key with a value (a KV pair). Maps do not allow duplicate keys and a key can map to only one value. The demonstration consists of three files:
#include <iostream> using namespace std; template <class K, class V> class KVTree { private: K key; // 1 V value; // 2 KVTree* left = nullptr; // 3 KVTree* right = nullptr; public: //KVTree() : left(nullptr), right(nullptr) {} // 4 ~KVTree(); V* insert(K& a_key, V& a_value); V* search(K& a_key); void list() { right->_list(); } // 5 private: void _list(); // 6 };
template <class K, class V> KVTree<K, V>::~KVTree() { if (left != nullptr) delete left; if (right != nullptr) delete right; }
delete left
and delete right
each trigger a recursive call to the destructor function.
template <class K, class V> V* KVTree<K, V>::insert(K& key, V& value) { KVTree* root = this; // 1 KVTree* down = right; // 2 while (down != nullptr && down->key != key) // 3 { root = down; if (key < down->key) down = down->left; else down = down->right; } if (down != nullptr) // 4 return &down->value; down = new KVTree; // 5 down->key = key; down->value = value; // 6 if (key < down->key) root->left = down; else root->right = down; return &down->value; }
operator<
and operator==
are implemented for the key class that replaces the template variable K.
operator==
than it is is to implement operator!=
, so the equality operator is used with the negation operator ( !
)template <class K, class V> V* KVTree<K, V>::search(K& key) { KVTree* root = right; // 1 while (root != nullptr && root->key != key) // 2 { if (key < root->key) root = root->left; else root = root->right; } if (root == nullptr) // 2 return nullptr; else return &root->value; }
nullptr
.
nullptr
if the key is not found.template <class K, class V> void KVTree<K, V>::_list() { if (left != nullptr) left->_list(); cout << value << endl; if (right != nullptr) right->_list(); }
operator<<
is overloaded for both template classes that replaces K and V respectively. A more complete or authentic implementation would probably define two functions: one to list the keys and one to list the values.
#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) {} friend ostream& operator<<(ostream& out, Employee& me) { out << me.name << " " << me.id; return out; } };
operator<<
.
#include <iostream> #include <string> #include "KVTree.h" #include "Employee.h" using namespace std; int main() { KVTree<string, Employee> tree; string name; // Key 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': case 'n': { cout << "Employee name: "; getline(cin, name); int number; cout << "Employee Number: "; cin >> number; cin.ignore(); Employee e(name, number); // Value tree.insert(name, e); break; } case 'S': case 's': { cout << "Employee name: "; getline(cin, name); Employee* e = tree.search(name); if (e != nullptr) cout << "Employee: " << *e << endl; else cout << name << " NOT FOUND" << endl; break; } case 'L': case 'l': tree.list(); break; case 'E': case 'e': return 0; //exit(0); // ends the program but doesn't call the destructor default: cerr << "Unrecognized operation: \"" << operation << "\"" << endl; break; } } return 0; }