Оператор взятия индекса переопределен в классах Array_RC и Array_Sort, и обе реализации имеют равный приоритет. Поэтому внутри Array_RC_S неквалифицированное обращение к оператору взятия индекса неоднозначно. Класс Array_RC_S должен предоставить собственную реализацию, иначе пользователи не смогут напрямую применять такой оператор к объектам этого класса. Но какова семантика его вызова в Array_RC_S? При учете отсортированности массива он должен установить в true унаследованный член dirty_bit. А чтобы учесть наследование от класса с контролем выхода за границы массива – проверить указанный индекс. После этого можно возвращать элемент массива с данным индексом. Последние два шага выполняет унаследованный из Array_RC оператор взятия индекса. При обращении
return Array_RCType::operator[]( index );
он вызывается явно, и механизм виртуализации не применяется. Поскольку это встроенная функция, то при статическом вызове компилятор подставляет ее код в место вызова.
Теперь протестируем нашу реализацию с помощью функции try_array(), передавая ей по очереди классы, конкретизированные из шаблона Array_RC_S типами int и string:
#include "Array_RC_S.h"
#include "try_array.C"
#include string
int 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_RC_Sint iA( ia,10 );
Array_RC_Sstring SA( sa,7 );
cout "eiie?aoecaoey eeanna Array_RC_Sint"
endl;
try_array( iA );
cout "eiie?aoecaoey eeanna Array_RC_S"string"
endl;
try_array( SA );
return 0;
}
Вот что печатает программа для класса, конкретизированного типом string (теперь ошибка выхода за границы массива перехватывается):
конкретизация класса Array_Sortstring
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
Assertion failed: ix = 0 && ix & size
Представленная в этой главе реализация иерархии класса Array иллюстрирует применение множественного и виртуального наследования. Детально проектирование класса массива описано в [NACKMAN94]. Однако, как правило, достаточно класса vector из стандартной библиотеки.
Упражнение 18.16
Добавьте в Array функцию-член spy(). Она запоминает операции, примененные к объекту класса: число доступов по индексу; количество вызовов каждого члена; какой элемент искали с помощью find() и сколько было успешных поисков. Поясните свои проектные решения. Модифицируйте все подтипы Array так, чтобы spy() можно было использовать и для них тоже.
Упражнение 18.17
Стандартный библиотечный класс map (отображение) называют еще ассоциативным массивом, поскольку он поддерживает индексирование значением ключа. Как вы думаете, является ли ассоциативный массив кандидатом на роль подтипа нашего класса Array? Почему?
Упражнение 18.18
Перепишите иерархию Array, пользуясь контейнерными классами из стандартной библиотеки и применяя обобщенные алгоритмы.
19. Применение наследования в C++