Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Порождение класса отсортированного массива



Вторая наша специализация класса Array – отсортированный подтип Array_Sort. Мы поместим его определение в заголовочный файл Array_S.h:

#ifndef ARRAY_S_H_

#define ARRAY_S_H_

 

#include " Array.h"

 

template < class Type>

class Array_Sort: public virtual Array< Type> {

protected:

void set_bit() { dirty_bit = true; }

void clear_bit() { dirty_bit = false; }

 

void check_bit() {

    if ( dirty_bit ) {

         sort( 0, Array< Type>:: _size-1 );

         clear_bit();

    }

}

 

public:

Array_Sort( const Array_Sort& );

Array_Sort( int sz = Array< Type>:: ArraySize )

        : Array< Type> ( sz )

           { clear_bit(); }

 

Array_Sort( const Type* arr, int sz )

        : Array< Type> ( arr, sz )

         { sort( 0, Array< Type>:: _size-1 ); clear_bit(); }

 

Type& operator[]( int ix )

   { set_bit(); return ia[ ix ]; }

 

void print( ostream& os = cout ) const

      { check_bit(); Array< Type>:: print( os ); }

Type min() { check_bit(); return ia[ 0 ]; }

Type max() { check_bit(); return ia[ Array< Type>:: _size-1 ]; }

 

bool is_dirty() const { return dirty_bit; }

int find( Type );

void grow();

 

protected:

bool dirty_bit;

};

 

#endif

Array_Sort включает дополнительный член – dirty_bit. Если он установлен в true, то не гарантируется, что массив по-прежнему отсортирован. Предоставляется также ряд вспомогательных функций доступа: is_dirty() возвращает значение dirty_bit; set_bit() устанавливает dirty_bit в true; clear_bit() сбрасывает dirty_bit в false; check_bit() пересортировывает массив, если dirty_bit равно true, после чего сбрасывает его в false. Все операции, которые потенциально могут перевести массив в неотсортированное состояние, вызывают set_bit().

При каждом обращении к шаблону Array необходимо указывать полный список параметров.

Array< Type>:: print( os );

вызывает функцию-член print() базового класса Array, конкретизированного одновременно с Array_Sort. Например:

Array_Sort< string> sas;

конкретизирует типом string оба шаблона: Array_Sort и Array.

cout < < sas;

конкретизирует оператор вывода из класса Array, конкретизированного типом string, затем этому оператору передается строка sas. Внутри оператора вывода инструкция

ar.print( os );

приводит к вызову виртуального экземпляра print() класса Array_Sort, конкретизированного типом string. Сначала вызывается check_bit(), а затем статически вызывается функция-член print() класса Array, конкретизированного тем же типом. (Напомним, что под статическим вызовом понимается разрешение функции на этапе компиляции и – при необходимости – ее подстановка в место вызова.) Виртуальная функция обычно вызывается динамически в зависимости от фактического типа объекта, адресуемого ar. Механизм виртуализации подавляется, если она вызывается явно с помощью оператора разрешения области видимости, как в Array:: print(). Это повышает эффективность в случае, когда мы явно вызываем экземпляр виртуальной функции базового класса из экземпляра той же функции в производном, например в print() из класса Array_Sort (см. раздел 17.5).

Функции-члены, определенные вне тела класса, помещены в файл Array_S.C. Объявление может показаться слишком сложным из-за синтаксиса шаблона. Но, если не считать списков параметров, оно такое же, как и для обычных классов:

template < class Type>

Array_Sort< Type>::

Array_Sort( const Array_Sort< Type> & as )

    : Array< Type> ( as )

{

// замечание: as.check_bit() не работает!

// ---- объяснение см. ниже...

if ( as.is_dirty() )

    sort( 0, Array< Type>:: _size-1 );

clear_bit();

}

Каждое использование имени шаблона в качестве спецификатора типа должно быть квалифицировано полным списком параметров. Следует писать:

template < class Type>

Array_Sort< Type>::

Array_Sort( const Array_Sort< Type> & as )

а не

template < class Type>

Array_Sort< Type>::

Array_Sort< Type> ( // ошибка: это не спецификатор типа

поскольку второе вхождение Array_Sort синтаксически является именем функции, а не спецификатором типа.

Есть две причины, по которым правильна такая запись:

if ( as.is_dirty() )

sort( 0, _size );

а не просто

as.check_bit();

Первая причина связана с типизацией: check_bit() – это неконстантная функция-член, которая модифицирует объект класса. В качестве аргумента передается ссылка на константный объект. Применение check_bit() к аргументу as нарушает его константность и потому воспринимается компилятором как ошибка.

Вторая причина: копирующий конструктор рассматривает массив, ассоциированный с as, только для того, чтобы выяснить, нуждается ли вновь созданный объект класса Array_Sort в сортировке. Напомним, однако, что член dirty_bit нового объекта еще не инициализирован. К началу выполнения тела конструктора Array_Sort инициализированы только члены ia и _size, унаследованные от класса Array. Этот конструктор должен с помощью clear_bit() задать начальные значения дополнительных членов и, вызвав sort(), обеспечить специальное поведение подтипа. Конструктор Array_Sort можно было бы инициализировать и по-другому:

// альтернативная реализация

template < class Type>

Array_Sort< Type>::

Array_Sort( const Array_Sort< Type> & as )

    : Array< Type> ( as )

{

dirty_bit = as.dirty_bit;

clear_bit();

}

Ниже приведена реализация функции-члена grow().1 Наша стратегия состоит в том, чтобы воспользоваться имеющейся в базовом классе Array реализацией для выделения дополнительной памяти, а затем пересортировать элементы и сбросить dirty_bit:

template < class Type>

void Array_Sort< Type>:: grow()

{

Array< Type>:: grow();

sort( 0, Array< Type>:: _size-1 );

clear_bit();

}

Так выглядит реализация двоичного поиска в функции-члене find() класса Array_Sort:

template < class Type>

int Array_Sort< Type>:: find( const Type & val )

{

int low = 0;

int high = Array< Type>:: _size-1;

check_bit();

while ( low < = high ) {

     int mid = ( low + high )/2;

 

     if ( val == ia[ mid ] )

          return mid;

 

     if ( val < ia[ mid ] )

          high = mid-1;

     else low = mid+1;

}

return -1;

}

Протестируем нашу реализацию класса Array_Sort с помощью функции try_array(). Показанная ниже программа тестирует шаблон этого класса для конкретизаций типами int и string:

#include " Array_S.C"

#include " try_array.C"

#include < string>

 

main()

{

static int ia[ 10 ] = { 12, 7, 14, 9, 128, 17, 6, 3, 27, 5 };

static string sa[ 7 ] = {

      " Eeyore", " Pooh", " Tigger",

      " Piglet", " Owl", " Gopher", " Heffalump"

};

 

Array_Sort< int> iA( ia, 10 );

Array_Sort< string> SA( sa, 7 );

 

cout < < " ê î í ê ð å ò è ç à ö è ÿ ê ë à ñ ñ à Array_Sort< int> "

    < < endl;

try_array( iA );

 

cout < < " ê î í ê ð å ò è ç à ö è ÿ ê ë à ñ ñ à Array_Sort< string> "

    < < endl;

try_array( SA );

 

return 0;

}

При конкретизации типом string после компиляции и запуска программа печатает следующий текст (обратите внимание, что попытка вывести элемент с индексом -1 заканчивается крахом):

 

конкретизация класса Array_Sort< string>

 

try_array: начальные значения массива

( 7 )< Eeyore, Gopher, Heffalump, Owl, Piglet, Pooh

  Tigger >

 

try_array: после присваиваний

( 7 )< Eeyore, Gopher, Owl, Piglet, Pooh, Pooh

  Pooh >

 

try_array: почленная инициализация

( 7 )< Eeyore, Gopher, Owl, Piglet, Pooh, Pooh

  Pooh >

 

try_array: после почленного копирования

( 7 )< Eeyore, Piglet, Owl, Piglet, Pooh, Pooh

  Pooh >

try_array: после вызова grow

( 7 )< < empty>, < empty>, < empty>, < empty>, Eeyore, Owl

  Piglet, Piglet, Pooh, Pooh, Pooh >

 

искомое значение: Tigger      возвращенный индекс: -1

Memory fault (coredump)

 

После почленного копирования массив не отсортирован, поскольку виртуальная функция вызывалась через объект, а не через указатель или ссылку. Как было сказано в разделе 17.5, в таком случае вызывается экземпляр функции из класса именно этого объекта, а не того подтипа, который может находиться в переменной. Поэтому функция sort() никогда не будет вызвана через объект Array. (Разумеется, мы реализовали такое поведение только в целях демонстрации.)


Поделиться:



Последнее изменение этой страницы: 2019-04-09; Просмотров: 271; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.032 с.)
Главная | Случайная страница | Обратная связь