virtual void print( ostream& = cout );

Type at( int ix ) const { return ia[ ix ]; }

virtual Type& operator[]( int ix ) { return ia[ix]; }

virtual void sort( int,int );

virtual int find( Type );

virtual Type min();

virtual Type max();

protected:

void swap( int, int );

void init( const Type*, int );

int _size;

Type *ia;

};

#endif

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

int find( const Array int &ia, int value )

{

for ( int ix = 0; ix ia.size(); ++ix )

// а теперь вызов виртуальной функции

if ( ia[ ix ] == value )

return ix;

return -1;

}

Для повышения производительности мы включили встроенную функцию-член at(),обеспечивающую прямое чтение элемента.

<p>18.6.1. Порождение класса, контролирующего выход за границы массива</p>

В функции try_array() из раздела 16.13, предназначенной для тестирования нашей предыдущей реализации шаблона класса Array, есть две инструкции:

int index = iA.find( find_val );

Type value = iA[ index ];

find() возвращает индекс первого вхождения значения find_val или -1, если значение в массиве не найдено. Этот код некорректен, поскольку в нем не проверяется, что не была возвращена -1. Поскольку -1 находится за границей массива, то каждая инициализация value может привести к ошибке. Поэтому мы создадим подтип Array, который будет контролировать выход за границы массива, – Array_RC и поместим его определение в заголовочный файл Array_RC.h:

#ifndef ARRAY_RC_H

#define ARRAY_RC_H

#include "Array.h"

template class Type

class Array_RC : public virtual ArrayType {

public:

Array_RC( int sz = ArraySize )

: Array Type( sz ) {}

Array_RC( const Array_RC& r );

Array_RC( const Type *ar, int sz );

Type& operator[]( int ix );

};

#endif

Внутри определения производного класса каждая ссылка на спецификатор типа шаблона базового должна быть квалифицирована списком формальных параметров:

Array_RC( int sz = ArraySize )

: ArrayType( sz ) {}

Такая запись неправильна:

// ошибка: Array - это не спецификатор типа

Array_RC( int sz = ArraySize ) : Array( sz ) {}

Единственное отличие поведения класса Array_RC от базового состоит в том, что оператор взятия индекса контролирует выход за границы массива. Во всех остальных отношениях можно воспользоваться уже имеющейся реализацией шаблона класса Array. Напомним, однако, что конструкторы не наследуются, поэтому в Array_RC определен собственный набор из трех конструкторов. Мы сделали класс Array_RC виртуальным наследником класса Array, поскольку предвидели необходимость множественного наследования.

Вот полная реализация функций-членов Array_RC, находящаяся в файле Array_RC.C (определения функций класса Array помещены в заголовочный файл Array.C, поскольку мы пользуемся моделью конкретизации шаблонов с включением, описанной в разделе 16.18):

#include "Array_RC.h"

#include "Array.C"

#include assert.h

template class Type

Array_RC Type ::Array_RC( const Array_RCType &r )

: Array Type( r ) {}

template class Type

Array_RC Type ::Array_RC( const Type *ar, int sz )

: Array Type( ar, sz ) {}

template class Type

Type &Array_RC &Type&::operator[]( int ix ) {

assert( ix = 0 && ix & Array &Type&::_size );

return ia[ ix ];

}

Мы квалифицировали обращения к членам базового класса Array, например к _size, чтобы предотвратить просмотр Array до момента конкретизации шаблона:

Array Type::_size;

Перейти на страницу:

Похожие книги