class PeekbackStack {
private:
const int static bos = -1;
public:
explicit PeekbackStack( int size ) :
stack( size ), _top( bos ) {}
bool empty() const { return _top == bos; }
bool full() const { return _top == size()-1; }
int top() const { return _top; }
int pop() {
if ( empty() )
/* обработать ошибку */ ;
return stack[ _top-- ];
}
void push( int value ) {
if ( full() )
/* обработать ошибку */ ;
stack[ ++_top ] = value;
}
bool peekback( int index, int &value ) const;
private:
int _top;
IntArray stack;
};
inline bool
PeekbackStack::
peekback( int index, int &value ) const
{
if ( empty() )
/* обработать ошибку */ ;
if ( index 0 || index _top )
{
value = stack[ _top ];
return false;
}
value = stack[ index ];
return true;
}
* Решая, следует ли использовать при проектировании класса с отношением "СОДЕРЖИТ" композицию или закрытое наследование, можно руководствоваться такими соображениями: если мы хотим заместить какие-либо виртуальные функции базового класса, то должны закрыто наследовать ему;
* если мы хотим разрешить нашему классу ссылаться на класс из иерархии типов, то должны использовать композицию по ссылке (мы подробно расскажем о ней в разделе 18.3.4);
* если, как в случае с классом PeekbackStack, мы хотим воспользоваться готовой реализацией, то композиция по значению предпочтительнее наследования. Если требуется отложенное выделение памяти для объекта, то следует выбрать композицию по ссылке (с помощью указателя).
18.3.2. Открытие отдельных членов
Когда мы применили закрытое наследование класса PeekbackStack от IntArray, то все защищенные и открытые члены IntArray стали закрытыми членами PeekbackStack. Было бы полезно, если бы пользователи PeekbackStack могли узнать размер стека с помощью такой инструкции:
is.size();
Разработчик способен оградить некоторые члены базового класса от эффектов неоткрытого наследования. Вот как, к примеру, открывается функция-член size() класса IntArray:
class PeekbackStack : private IntArray {
public:
// сохранить открытый уровень доступа
using IntArray::size;
// ...
};
Еще одна причина для открытия отдельных членов заключается в том, что иногда необходимо разрешить доступ к защищенным членам закрыто унаследованного базового класса при последующем наследовании. Предположим, что пользователям нужен подтип стека PeekbackStack, который может динамически расти. Для этого классу, производному от PeekbackStack, понадобится доступ к защищенным элементам ia и _size класса IntArray:
template class Type
class PeekbackStack : private IntArray {
public:
using intArray::size;
// ...
protected:
using intArray::size;
using intArray::ia;
// ...
};
Производный класс может лишь вернуть унаследованному члену исходный уровень доступа, но не повысить или понизить его по сравнению с указанным в базовом классе.
На практике множественное наследование очень часто применяется для того, чтобы унаследовать открытый интерфейс одного класса и закрытую реализацию другого. Например, в библиотеку классов Booch Components включена следующая реализация растущей очереди Queue (см. также статью Майкла Вило (Michaeel Vilot) и Грейди Буча (Grady Booch) в [LIPPMAN96b]):
template class item, class container
class Unbounded_Queue:
private Simple_Listitem , // ?aaeecaoey
public Queue item // eioa?oaen
{ ... }
18.3.3. Защищенное наследование
Третья форма наследования – это защищенное наследование. В таком случае все открытые члены базового класса становятся в производном классе защищенными, т.е. доступными из его дальнейших наследников, но не из любого места программы вне иерархии классов. Например, если бы нужно было унаследовать PeekbackStack от Stack, то закрытое наследование
// увы: при этом не поддерживается дальнейшее наследование
// PeekbackStack: все члены IntArray теперь закрыты
class Stack : private IntArray { ... }
было бы чересчур ограничительным, поскольку закрытие членов IntArray в классе Stack делает невозможным их последующее наследование. Для того чтобы поддержать наследование вида:
class PeekbackStack : public Stack { ... };