13. Шаблони и вложени класове

  Шаблони.
Класове-шаблони дават възможност да се използват данни от произволен тип. Вече сме използвали класове-шаблони в:
vector<int> v_i; 
vector<double> v_d;
vector<Employee> v_e;
За да дефинираме клас-шаблон, означаваме произволен тип с T и добавяме template<typename T> преди дефиницията на класа. Нека дефинираме наредена двойка елементи с данни от произволен тип:
template<typename T>
class Pair {
public:
Pair(T a, T b);
T get_first() const;
T get_second() const;
private:
T first;
T second;
};
Всички член-функции се дефинират също като шаблони.
// pairs.cpp 
#include <iostream>
#include <string>
using namespace std;

template<typename T>
class Pair
{
public:
Pair(T a, T b);
T get_first()
const;
T get_second()
const;
void print() const;
private:
T first;
T second;
};

template<typename T>
Pair<T>::Pair(T a, T b)
{ first = a;
second = b;
}

template<typename T>
T Pair<T>::get_first()
const
{ return first;
}

template<typename T>
T Pair<T>::get_second()
const
{ return second;
}

template<typename T>
void Pair<T>::print() const
{ cout << "Pair: (" << first << ","
<< second << ")" << endl;
}

int main()
{ Pair<
int> integers(10,22);
integers.print();
Pair<
double> doubles(1.5, 2.25);
doubles.print();
Pair<string> strings(
"One", "Two");
strings.print();
char ch; cin >> ch;
return 0;
}

 Класът  List (list2.cpp, list0.cpp) съхранява свързан списък от низове. Използвайки шаблони,  List ще може да съхранява стойности от произволен тип, както това става в стандартния клас list от STL. За тази цел дефинираме шаблон на клас, като задаваме формален параметър T на шаблона с конструкцията

template<typename T>
class List;


При създаване на обект от този клас задаваме фактически параметър string на шаблона по познатата схема

List<string> staff;

Лесно е да се направи и дефиницията на класа шаблон:
template<typename T>
class List {
public:
List();
void push_back(T s);
void insert(Iterator<T> pos, T s);
void erase(Iterator<T> pos);
Iterator<T> begin();
Iterator<T> end();
private:
Node<T>* first;
Node<T>* last;
};
class List {
public:
List();
void push_back(string s);
void insert(Iterator pos, string s);
Iterator erase(Iterator pos);
Iterator begin();
Iterator end();
private:
Node* first;
Node* last;
};

Ще пренапишем класовете, за свързан списък като използваме шаблони.

// list.cpp
#nclude <iostream>
#include <string>
#include <cassert>
using namespace std;

template<typename T>
class List;

template<typename T>
class Iterator;

template<typename T>
class Link  {
public:
   Link(T s);
private:
   T data;
   Link<T> *previous;
   Link<T> *next;
friend class List<T>;
friend class Iterator<T>;
};

template<typename T>
class List   {
public:
   List();
   void push_back(T);
   void insert(Iterator<T>, T);
   void erase(Iterator<T>);
   Iterator<T> begin();
   Iterator<T> end();
private:
   Link<T> *first;
   Link<T> *last;
};

template<typename T>
class Iterator   {
public:
   Iterator();
   T operator*() const;
   void operator++(int);
   void operator--(int);
   bool operator!=(Iterator<T>) const;
private:
   Link<T> *position;
   Link<T> *last;
friend class List<T>;
};

template<typename T>
Link<T>::Link(T s)
{ data = s;
  previous = NULL;
  next = NULL; }

template<typename T>
List<T>::List()
{ first = NULL; last = NULL; }

template<typename T>
void List<T>::push_back(T s)
{ Link<T> *newlink = new Link<T>(s);
  if (last == NULL)
  { first = newlink; last = newlink; }
  else
  { newlink->previous = last;
    last->next = newlink;
    last = newlink;
  }
}

template<typename T>
void List<T>::insert(Iterator<T> iter, T s)
{ if (iter.position == NULL)
  { push_back(s); return; }

  Link<T> *after = iter.position;
  Link<T> *before = after->previous;
  Link<T> *newlink = new Link<T>(s);
  newlink->previous = before;
  newlink->next = after;
  after->previous = newlink;
  if (before == NULL) first = newlink;
  else         before->next = newlink;
}

template<typename T>
void List<T>::erase(Iterator<T> iter)
{ assert(iter.position != NULL);
  Link<T> *remove = iter.position;
  Link<T> *before = remove->previous;
  Link<T> *after = remove->next;
  if (remove == first) first = after;
  else          before->next = after;
  if (remove == last) last = before;
  else     after->previous = before;
  iter.position = after;
  delete remove;
}

template<typename T>
Iterator<T> List<T>::begin()
{ Iterator<T> iter;
  iter.position = first;
  iter.last = last;
  return iter;
}

template<typename T>
Iterator<T> List<T>::end()
{ Iterator<T> iter;
  iter.position = NULL;
  iter.last = last;
  return iter;
}

template<typename T>
Iterator<T>::Iterator()
{ position = NULL; last = NULL; }

template<typename T>
T Iterator<T>::operator*() const
{ assert(position != NULL);
  return position->data;
}

template<typename T>
void Iterator<T>::operator++(int dummy)
{ assert(position != NULL);
  position = position->next;
}

template<typename T>
void Iterator<T>::operator--(int dummy)
{ if (position == NULL)  position = last;
  else                   position = position->previous;
  assert(position != NULL);
}

template<typename T>
bool Iterator<T>::operator!=(Iterator<T> b) const
{ return position != b.position; }

int main()
{ List<string> staff;
  staff.push_back("Cracker, Carl");
  staff.push_back("Hacker, Harry");
  staff.push_back("Lam, Larry");
  staff.push_back("Sandman, Susan");

/* добавя елемент на четвърто място */
  Iterator<string> pos;
  pos = staff.begin();
  pos++; pos++; pos++;
  staff.insert(pos, "Reindeer, Rudolf");

/* отстранява втория елемент */
  pos = staff.begin();
  pos++;
  staff.erase(pos);

/* отпечатва свързания списък */
  for (pos = staff.begin(); pos != staff.end(); pos++)
     cout << *pos << "\n";
   return 0;
}

Cracker, Carl
Lam, Larry
Reindeer, Rudolf
Sandman, Susan

Вложени класове

В STL класът iterator е дефиниран в класа list:

list<string>::iterator pos = staff.begin();

За да се вложи един клас в друг, вътрешният клас се дефинира във външния клас:

class List {
...
class Iterator;
...
};

Пример на клaса List със същия интерфейс, както и класа list от STL.

//list1.cpp

#include <string>
#include <iostream>
#include <cassert>
using namespace std;

template<typename T> class List;

template<typename T>
class Node {
public:
   Node(T s);
private:
   T data;
   Node<T>* previous;
   Node<T>* next;
friend class List<T>;
friend class List<T>::Iterator;
};
  
template<typename T>
class List {
public:
   List();
   List(const List<T>& b);
   ~List();
   List<T>& operator=(const List<T>& b);
   class Iterator;

   void push_back(T s);
   void insert(Iterator iter, T s);
   Iterator erase(Iterator i);
   Iterator begin();
   Iterator end();
private:
   void copy(const List<T>& b);
   void free();
   Node<T>* first;
   Node<T>* last;
};

template<typename T>
class List<T>::Iterator {
public:
   Iterator();
   T operator*() const;
   void operator++(int dummy);
   void operator--(int dummy);
   bool operator==(Iterator b) const;
   bool operator!=(Iterator b) const;
private:
   Node<T>* position;
   Node<T>* last;
friend class List<T>;
};

template<typename T>
List<T>::List()
{  first = NULL;
   last = NULL;
}

template<typename T>
List<T>::~List()
{  free();
}

template<typename T>
List<T>::List(const List<T>& b)
{  first = NULL;
   last = NULL;
   copy(b);
}

template<typename T>
List<T>& List<T>::operator=(const List<T>& b)
{  if (this != &b)
   {  free();
      copy(b);
   }
   return *this;
}

template<typename T>
void List<T>::push_back(T s)
{  Node<T>* newnode = new Node<T>(s);
   if (last == NULL) /* list is empty */
   {  first = newnode;
      last = newnode;
   }
   else
   {  newnode->previous = last;
      last->next = newnode;
      last = newnode;
   }
}

template<typename T>
void List<T>::insert(Iterator iter, T s)
{  if (iter.position == NULL)
   {  push_back(s);
      return;
   }
   Node<T>* after = iter.position;
   Node<T>* before = after->previous;
   Node<T>* newnode = new Node<T>(s);
   newnode->previous = before;
   newnode->next = after;
   after->previous = newnode;
   if (before == NULL) /* insert at beginning */
      first = newnode;
   else
      before->next = newnode;
}

template<typename T>
typename List<T>::Iterator List<T>::erase(Iterator i)
{  Iterator iter = i;
   assert(iter.position != NULL);
   Node<T>* remove = iter.position;
   Node<T>* before = remove->previous;
   Node<T>* after = remove->next;
  
   if (remove == first) first = after;
   else          before->next = after;
   if (remove == last) last = before;
   else     after->previous = before;
  
   iter.position = after;
   delete remove;
   return iter;
}

template<typename T>
typename List<T>::Iterator List<T>::begin()
{  Iterator iter;
   iter.position = first;
   iter.last = last;
   return iter;
}

template<typename T>
typename List<T>::Iterator List<T>::end()
{  Iterator iter;
   iter.position = NULL;
   iter.last = last;
   return iter;
}

template<typename T>
List<T>::Iterator::Iterator()
{  position = NULL;
   last = NULL;
}

template<typename T>
T List<T>::Iterator::operator*() const
{  assert(position != NULL);
   return position->data;
}

template<typename T>
void List<T>::Iterator::operator++(int dummy)
{  assert(position != NULL);
   position = position->next;
}

template<typename T>
void List<T>::Iterator::operator--(int dummy)
{  if (position == NULL) position = last;
   else                  position = position->previous;
   assert(position != NULL);
}

template<typename T>
bool List<T>::Iterator::operator==(Iterator b) const
{  return position == b.position;
}

template<typename T>
bool List<T>::Iterator::operator!=(Iterator b) const
{  return position != b.position;
}

template<typename T>
Node<T>::Node(T s)
{  data = s;
   previous = NULL;
   next = NULL;
}

template<typename T>
void List<T>::copy(const List<T>& b)
{  for (Iterator p = b.begin(); p != b.end(); p++)
      push_back(*p);
}

template<typename T>
void List<T>::free()
{  while (begin() != end()) erase(begin());
}

int main()
{  List<string> staff;

   staff.push_back("Cracker, Carl");
   staff.push_back("Hacker, Harry");
   staff.push_back("Lam, Larry");
   staff.push_back("Sandman, Susan");

   /* add a value in fourth place */
   List<string>::Iterator pos;
   pos = staff.begin();
   pos++;
   pos++;
   pos++;

   staff.insert(pos, "Reindeer, Rudolf");

   /* remove the value in second place */
   pos = staff.begin();
   pos++;

   staff.erase(pos);

   /* print all values */
   for (pos = staff.begin(); pos != staff.end(); pos++)
      cout << *pos << "\n";

   return 0;
}