Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

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




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

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

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

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

Ниже приведен пример простой программы, демонстрирующей наследование шаблонного класса 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;

}

 

Классы поддерживающие транзакции

Концепция smart-указателей позволяет просто решать задачу поддержки транзакций.

 

§ Если транзакция началась, то все изменения в ней либо вносятся вместе, либо не вносятся вообще;

§ если клиент начал и не завершил транзакцию, то другие клиенты не видят его изменений;

§ две транзакции не могут одновременно менять одни и те же данные.

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

#include <iostream.h>

template <class T1>

Class Cls

{ T1 x;

public:

Cls(): x(0){}

f(int X) {x=X;}

T1 Show()

{return xl;}

};

// smart-pointer с поддержкой отмены.

template <class T2>

Class Tran

{ T2* that; // текущее значение объекта класса Cls

T2* prev; // предыдущее значение объекта класса Cls

public:

Tran():prev(NULL), that(new T2){}; // конструктор

Tran(const Tran & obj): // конструктор копирования

that(new T2(*(obj.that))), prev(NULL) {};

~Tran(){delete that; delete prev;}; // деструктор

Tran& operator=(const Tran & obj); // перегрузка присваивания

void Show(); // отображение значений (предыдущего и

// текущего) объекта класса Cls

void BeginTrans(); // начало транзакции

void Commit(); // закрепление

void DeleteTrans(); // отмена транзакции

T2* operator->(); // реализация указателя

};

template <class T2>

Tran<T2>& Tran<T2>::operator=(const Tran<T2> & obj)

{ if (this!= &obj) // проверка на случай obj=obj

{ delete that; // удаление текущего значения объекта

that = new T2(*(obj.that)); // создание и копирование

}

return *this;

}

template <class T2>

T2* Tran<T2>::operator->() {return that;}

template <class T2>

void Tran<T2>::Show()

{ cout<<"состояния объекта"<<endl;

if (prev) cout<<"prev = "<<prev->get()<<endl;

cout<<"that = "<<that->get()<<endl;

}

template <class T2>

void Tran<T2>::BeginTrans()

{ delete prev; // удаление предыдущего значения

prev = that; // текущее становится предыдущим

that = new T2(*prev); // новое значение текущего значения

}

template <class T2>

void Tran<T2>::Commit ()

{ delete prev; // удаление предыдущего значения

prev = NULL; // предыдущего состояния нет

}

template <class T2>

void Tran<T2>::DeleteTrans()

{ if (prev!= NULL)

{ delete that; // удаление текущего значения

that = prev; // предыдущее становится текущим

prev = NULL; // предыдущего состояния нет

}

}

int main (void)

{ Tran<Cls<int> > tr;

tr->f(5);

tr.Show();

tr.BeginTrans();

tr.Show();

tr->f(7);

tr.Show();

tr.DeleteTrans();

tr.Show();

tr.BeginTrans();

tr.Show();

tr->f(7);

tr.Commit();

tr.Show();

return 0;

}

Результат работы программы:

состояния объекта

that = 5

состояния объекта

prev = 5

that = 5

состояния объекта

prev = 5

that = 7

состояния объекта

that = 5

состояния объекта

prev = 5

that = 5

состояния объекта

that = 7

Следует отметить, что перегрузка операции присваивания (=) понадобится в случае, например, если описаны два объекта.

Tran<Cls<int> > tr,ts;

...

ts=tr; // требуется перегрузка операции =

 

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...