Совместное использование шаблонов и наследования

Шаблонные классы, как и обычные, могут использоваться повторно. Шаблоны и наследование представляют собой механизмы повторного использования кода и могут включать полиморфизм. Шаблоны и наследования связаны между собой следующим образом:

- шаблон класса может быть порожден от обычного класса;

- шаблонный класс может быть производным от шаблонного класса;

- обычный класс может быть производным от шаблона класса.

Ниже приведен пример простой программы, демонстрирующей наследование шаблонного класса oper от шаблонного класса vect.

#include "iostream.h"

template <class T>

class vect// класс-вектор

{protected:

T *ms; // массив-вектор

int size; // размерность массива-вектора

public:

vect(int n) : size(n) // конструктор

{ ms=new T[size];}

~vect(){delete [] ms;} // деструктор

T &operator[](const int ind) // доопределение операции []

{ if((ind>0) && (ind<size)) return ms[ind];

else return ms[0];

}

};

template <class T>

class oper : public vect<T>// класс операций над вектором

{ public:

oper(int n): vect<T>(n) {} // конструктор

~oper(){} // деструктор

void print() // функция вывода содержимого вектора

{ for(int i=0;i<size;i++)

cout<<ms[i]<<' ';

cout<<endl;

}

};

void main()

{ oper <int> v_i(4); // int-вектор

oper <double> v_d(4); // double-вектор

v_i[0]=5; v_i[1]=3; v_i[2]=2; v_i[3]=4; // инициализация int

v_d[0]=1.3; v_d[1]=5.1; v_d[2]=.5; v_d[3]=3.5; // инициализация double

cout<<"int вектор = ";

v_i.print();

cout<<"double вектор = ";

v_d.print();

}

Как следует из примера, реализация производного класса от класса- шаблона в основном ничем не отличается от обычного наследования.

Шаблоны класса и friend

Для шаблонов класса, как и для обычных классов, могут быть установлены отношения дружественности. Дружественность может быть установлена между шаблонным классом и глобальной функцией, функцией-членом другого (возможно шаблонного класса) или целым классом (возможно шаблонным).

Некоторые примеры использования шаблона класса

Реализация smart-указателя

При использовании в программе указателя на объект, память для которого выделена с помощью оператора new, в случае если объект становится не нужен, то для его разрушения необходимо явно вызвать оператор delete. В то же время на один объект могут ссылаться множество указателей и, следовательно, нельзя однозначно сказать, нужен ли еще этот объект или он уже может быть уничтожен. Рассмотрим пример реализации класса, для которого происходит уничтожение объектов, в случае если уничтожается последняя ссылка на него. Это достигается тем, что наряду с указателем на объект хранится счетчик числа других указателей на этот же объект. Объект может быть уничтожен только в том случае, если счетчик ссылок станет равным нулю.

#include "iostream.h"

#include "string.h"

template <class T>

struct Status // состояние указателя

{ T *RealPtr; // указатель

int Count; // счетчик числа ссылок на указатель

};

template <class T>

class Point // класс-указатель

{ Status<T> *StatPtr;

public:

Point(T *ptr=0); // конструктор

Point(const Point &); // копирующий конструктор

~Point();

Point &operator=(const Point &); // перегрузка =

// Point &operator=(T *ptr); // перегрузка =

T *operator->() const;

T &operator*() const;

};

Приведенный ниже конструктор Point инициализирует объект указателем. Если указатель равен NULL, то указатель на структуру Status, содержащую указатель на объект и счетчик других указателей, устанавливается в NULL. В противном случае создается структура Status

template <class T>

Point<T>::Point(T *ptr) // описание конструктора

{ if(!ptr) StatPtr=NULL;

else

{ StatPtr=new Status<T>;

StatPtr->RealPtr=ptr;

StatPtr->Count=1;

}

}

Копирующий конструктор StatPtr не выполняет задачу копирования исходного объекта, а так как новый указатель ссылается на тот же объект, то мы лишь увеличиваем число ссылок на объект.

template <class T> // описание конструктора копирования

Point<T>::Point(const Point &p):StatPtr(p.StatPtr)

{ if(StatPtr) StatPtr->Count++; // увеличено число ссылок

}

Деструктор уменьшает число ссылок на объект на 1, и при достижении значения 0 объект уничтожается

template <class T>

Point<T>::~Point() // описание деструктора

{ if(StatPtr)

{ StatPtr->Count--; // уменьшается число ссылок на объект

if(StatPtr->Count<=0) // если число ссылок на объект <=0,

{ delete StatPtr->RealPtr; // то уничтожается объект

delete StatPtr;

}

}

}

template <class T>

T *Point<T>::operator->() const

{ if(StatPtr) return StatPtr->RealPtr;

else return NULL;

}

template <class T>

T &Point<T>::operator*() const // доступ к StatPtr осуществляется

{ if(StatPtr) return *StatPtr->RealPtr; // посредством this-указателя

else throw bad_pointer; // исключительная ситуация

}

При выполнении присваивания вначале необходимо указателю слева от знака = отсоединиться от ”своего” объекта и присоединиться к объекту, на который указывает указатель справа от знака =.

template <class T>

Point<T> &Point<T>::operator=(const Point &p)

{ // отсоединение объекта слева от = от указателя

if(StatPtr)

{ StatPtr->Count--;

if(StatPtr->Count<=0) // так же, как и в деструкторе

{ delete StatPtr->RealPtr; // освобождение выделенной под объект

delete StatPtr; // динамической памяти

}

}

// присоединение к новому указателю

StatPtr=p.StatPtr;

if(StatPtr) StatPtr->Count++;

return *this;

}

Struct Str

{ int a;

char c;

};

void main()

{ Point<Str> pt1(new Str); // генерация класса Point, конструирование

// объекта pt1, инициализируемого указателем

// на стр-ру Str, далее с объектом можно обра-

// щаться как с указателем

Point<Str> pt2=pt1,pt3; // для pt2 вызывается конструктор копирования,

// затем создается указатель pt3

pt3=pt1; // pt3 переназначается на объект указателя pt1

(*pt1).a=12; // operator*() получает this указатель на pt1

(*pt1).c='b';

int X=pt1->a; // operator->() получает this-указатель на pt1

char C=pt1->c;

}

Наши рекомендации