Программирование >>  Решение нетривиальных задач 

1 ... 68 69 70 [ 71 ] 72 73 74 ... 77


long max( long, long );

Прототип вызывает расширение шаблона. Компилятор с легкостью преобразует аргумент типа int в long, даже если ему не нужно делать это преобразование для расширения шаблона.

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

156. Всегда знайте размер шаблона после его расширения

Большинство книг демонстрирует шаблоны типа простого контейнера массива, подобного показаному на листинге 13. Вы не можете использовать здесь наследование (скажем, с базовым классом array, от которого наследуется int array). Проблема заключается в перегрузке операции operator [](). Вы бы хотели, чтобы она была виртуальной функцией в базовом классе, замещенная затем в производном классе, но сигнатура версии производного класса должна отличаться от сигнатуры базового класса, чтобы все это заработало. Здесь определения функций должны отличаться лишь возвращаемыми типами: int array:: operator[]() должна возвращать ссылку на тип int, а long array:: operator[]() должна возвращать ссылку на тип long, и так далее. Так как время возврата не рассматривается как часть сигнатуры при выборе перегруженной функции, то реализация на основе наследования не жизнеспособна. Единственным решением является шаблон.

Листинг 13. Простой контейнер массива

1 template <class type, int size >

2 class array

4 type array[size];

5 public:

6 class out of bounds {}; возбуждается исключение, если

7 индекс за пределами массива

8 type &operator[](int index);

9 };

11 template <class type, int size >

12 inline type &array<type, size>:: operator[]( int index)

13 {

14 if( 0 <= index && index < size )

15 return array[ index ]



16 throw out of bounds;

17 }

Единственная причина осуществимости этого определения заключается в том, что функция-член является встроенной. Если бы этого не было, то вы могли бы получить значительное количество повторяющегося кода. Запомните, что везде далее происходит полное расширение шаблона, включая все функции-члены*. Вследствие того, что каждое из следующих определений на самом деле создает разный тип, то вы должны расширить этот шаблон четыре раза, генерируя четыре идентичные функции operator[]() , по одной для каждого расширения шаблона:

array<int,10> ten element array;

array<int,11> eleven element array;

array<int,12> twelve element array;

array<int,13> thirteen element array;

(то есть array<int,10>:: operator [](), array<int,11>:: operator []() и так далее).

Вопрос состоит в том, как сократить до минимума дублирование кода. Что, если мы уберем размер за пределы шаблона, как на листинге 14? Предыдущие объявления теперь выглядят так:

array<int> ten element array (10);

array<int> eleven element array (11);

array<int> twelve element array (12);

array<int> thirteen element array (13);

Теперь у нас есть только одно определение класса (и один вариант operator[]() ) с четырьмя объектами этого класса.

Листинг 14. Шаблон массива (второй проход)

1 template <class type>

2 class array

4 type *array;

5 int size;

6 public:

7 virtual ~array( void );

8 array( int size = 128 ); 9

10 class out of bounds {}; возбуждается исключение, если

11 индекс за пределами массива

12 type & operator[]( int index);

13 };

* Функции из шаблонов генерируется, только если они используются программе (по крайней мере, так должен поступать хороший компилятор). - Ред.



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

array< array<int, 10>, 20> ar;

(20-элементный массив из 10-элементных массивов). Определение на листинге 14 устанавливает размер массива, используя конструктор, поэтому лучшее, что вы можете получить, это: array< array<int> > ar2(20);

Внутренний array<int> создан с использованием конструктора по умолчанию, поэтому это 128-элементный массив; мы объявили 20-элементный массив из 128-элементных массивов.

Вы можете решить эту последнюю проблему при помощи наследования. Рассмотрим следующее определение производного класса:

template< class type, int size >

class sized array : public array<type>

public:

sized array() : array<type>(size) {}

Здесь ничего нет, кроме единственной встроенной функции, поэтому это определение очень маленького класса. Оно совсем не будет увеличивать размер программы, вне зависимости от того, сколько раз будет расширен шаблон. Вы теперь можете записать:

sized array< sized array<int,10>, 20> ar3;

15 template <class type>

16 array<type>::array( int sz /*= 128*/ ): size(sz)

17 , array( new type[ sz ] )

18 {} 19

20 template <class type>

21 array<type>::~array( void )

22 {

23 delete [] array;

24 } 25

26 template <class type>

27 inline type &array<type>:: operator[]( int index)

28 {

29 if( 0 <= index && index < size )

30 return array[ index ]

31 throw out of bounds; }



1 ... 68 69 70 [ 71 ] 72 73 74 ... 77

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика