|
Программирование >> Инициализация объектов класса, структура
#ifndef ARRAY H #define ARRAY H #include <iostream> / / необходимо для опережающего объявления operator template <class Type> class Array; template <class Type> ostreamS operator ( ostream S, AArray<Type> S ); template <class Type> class Array { static const int ArraySize = 12; public: explicit AArray( int sz = AArraySize ) { init( 0, sz ); } Array( const Type *ar, int sz ) { init( ar, sz ); } AArray( const AArray Si ) { init( i.ia, i.size()); } virtual ~Array() { delete[] ia; } AArrayS operator=( const AArray S ); int size() const { return size; } virtual void grow(); virtual void print( ostreamS = cout ); Type at( int ix ) const { return ia[ ix ]; } virtual TypeS 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 AArray< int > Sia, int value ) { for ( int ix = 0; ix < ia.size(); ++ix ) а теперь вызов виртуальной функции if ( ia[ ix ] == value ) return ix; return -1; на какой бы тип она ни ссылалась, было бы достаточно встроенного чтения элемента: int index = i.find( find val ); предыдущей реализации шаблона класса Array, есть две инструкции: Type value = [ index ]; find() возвращает индекс первого вхождения значения find val или -l, если значение в массиве не найдено. Этот код некорректен, поскольку в нем не проверяется, что не была возвращена -l. Поскольку -l находится за границей массива, то каждая инициализация value может привести к ошибке. Поэтому мы создадим подтип Array, который будет контролировать выход за границы массива, - Array RC и поместим его определение в #ifndef ARRAY RC H #define ARRAY RC H #include Array.h virtual Array<Type> { template <class Type> class Array RC : public public: AArray RC( int sz = AArraySize ) : Array<Type>( sz ) {} Array RC( const Array RC& r ); Array RC( const Type *ar, int sz ); Types operator[]( int ix ); заголовочный файл Array RC.h: #endif Внутри определения производного класса каждая ссылка на спецификатор типа шаблона Array RC( int sz = ArraySize ) базового должна быть квалифицирована списком формальных параметров: : Array<Type>( sz ) {} / / ошибка: AArray - это не спецификатор типа Такая запись неправильна: Array RC( int sz = ArraySize ) : Array( sz ) {} Для повышения производительности mi включили встроенную функцию-член at() ,обеспечивающую прямое чтение элемента. 18.6.1. Порождение класса, контролирующего выход за границы массива В функции try array() из раздела 16.13, предназначенной для тестирования нашей #include Array RC.h #include Array.C #include <assert.h> template <class Type> Array RC<Type>::Array RC( const Array RC<Type> Sr ) : 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 SS ix < AArray<Type>:: size ); return ia[ ix ]; 16.18): Мы квалифицировали обращения к членам базового класса Array, например к size, чтобы предотвратить просмотр Array до момента конкретизации шаблона: Array<Type>:: size; Мы достигаем этого, включая в обращение параметр шаблона. Таким образом, имена в определении Array RC разрешаются тогда, когда определяется шаблон (за исключением имен, явно зависящих от его параметра). Если встречается неквалифицированное имя size, то компилятор должен найти его определение, если только это имя не зависит явно от параметра шаблона. Мы сделали имя size зависящим от параметра шаблона, предварив его именем базового класса Array<Type>. Теперь компилятор не будет пытаться разрешить имя size до момента конкретизации шаблона. (В определении класса Array Sort мы приведем другие примеры использования подобных приемов.) Каждая конкретизация Array RC порождает экземпляр класса Array. Например: Array RC<string> sa; конкретизирует параметром string как шаблон Array RC, так и шаблон Array. Приведенная ниже программа вызывает try array() (реализацию см. в разделе 16.13), передавая ей объекты подтипа Array RC. Если все сделано правильно, то выходы за границы массивы будут замечены: Единственное отличие поведения класса Array RC от базового состоит в том, что оператор взятия индекса контролирует выход за границы массива. Во всех остальных отношениях можно воспользоваться уже имеющейся реализацией шаблона класса Array. Напомним, однако, что конструкторы не наследуются, поэтому в Array RC определен собственный набор из трех конструкторов. Мы сделали класс Array RC виртуальным наследником класса Array, поскольку предвидели необходимость множественного наследования. Вот полная реализация функций-членов Array RC, находящаяся в файле Array RC.C (определения функций класса Array помещены в заголовочный файл Array.C, поскольку мы пользуемся моделью конкретизации шаблонов с включением, описанной в разделе
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |