Анимация
JavaScript


Главная  Библионтека 

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 [ 71 ] 72 73 74 75 76

для того, чтобы получить 20-элементный массив из 10-элементных массивов.

157. Шаблоны классов должны обычно определять производные классы

158. Шаблоны не заменяют наследование; они его автоматизируют

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

Во-первых, давайте взглянем на то, что не нужно делать. Класс storable, уже использованный мной, снова представляется хорошим примером. Сначала создадим объект collection для управления сохраняемыми объектами:

class collection

storable *head; public: ...

storable *find( const storable &a match of this ) const;

storable *collection::find( const storable &a match of this ) const

Послать сообщение объекту начала списка, указывающее, что спи сок просматривается на совпадение со значением a match of this;

return head ? head->find( a match of this ) : NULL

Механизм поиска нужных объектов скрыт внутри класса storable. Вы можете изменить лежащую в основе структуру данных, поменяв определение storable, и эти изменения совсем не затронут реализацию класса collection.

Затем давайте реализуем класс storable, использующий простой связанный список в качестве лежащей в основе структуры данных:

class storable

storable *next, *prev;



13 В действительности я бы использовал множественное наследование с участием класса string. Использованный здесь код имеет цель немного упростить пример.

public:

storable *find ( const storable &match of this ) const; storable *successor ( void ) const;

virtual int operator== ( const storable &r ) const;

storable *storable::find( const storable &match of this ) const

Возвращает указатель на первый элемент в списке (начиная с себя), имеющий тот же ключ, что и match of this. Обычно, объект-коллекция должен послать это сообщение объекту начала списка, указатель на который хранится в классе коллекции.

storable *current = this;

for( ; current; current = current->next )

if( *current == match of this ) найдено совпадение return current;

storable *storable::successor( void ) const

Возвращает следующее значение в последовательности. return next;

Функция operator==() должна быть чисто виртуальной, потому что отсутствует возможность ее реализации на уровне класса storable. Реализация должна быть выполнена в производном классе13 :

class storable string : public storable

string s; public:

virtual int operator==( const storable &r ) const; ...

virtual int operator==( const storable &r ) const

storable string *right = dynamic cast<storable string *>( &r ); return right ? (s == r.s) : NULL;

Я здесь использовал предложенный в ISO/ANSI Си++ безопасный механизм нисходящего приведения типов. right инициализируется значением NULL, если передаваемый объект (r) не относится к типу storable string. Например, он может принадлежать к некоторому



См. пред1дущее примечание к правилу 156. - Ред.

другому классу, также являющемуся наследником storable.

Пока все идет хорошо. Теперь к проблемам, связанным с шаблонами. Кто-нибудь, не понимающий того, что делает, говорит: "Ребята, я могу исключить наследование и потребность в виртуальных функциях, используя шаблоны", а делает, вероятно, нечто подобное:

template <class t key>

class storable

storable *next, *prev;

t key key;

public: ...

storable *find ( const storable &match me ) const;

storable *successor ( void ) const;

int operator==( const storable &r ) const;

template <class t key>

int storable<t key>::operator==( const storable<t key> &r ) const

return key == r.key ;

template <class t key>

storable<t key> *storable<t key>::successor( void ) const

return next;

template <class t key>

storable *storable<t key>::find( const storable<t key>

&match me ) const

storable<t key> *current = this;

for( ; current; current = current->next )

if( *current == match me ) найдено совпадение

return current;

Проблема здесь в непроизводительных затратах. Функции-члены шаблона класса сами являются шаблонами функций. Когда компилятор расширяет шаблон storable, он также расширяет варианты всех функций-членов этого шаблона*. Хотя я их не показал, вероятно, в классе storable определено множество функций. Многие из этих функций будут похожи в том, что они не используют информацию о типе, передаваемую в шаблон.



0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 [ 71 ] 72 73 74 75 76