Бьерн Страуструп - Язык программирования С++. Вступление, глава 1 - Множественные реализации
ОГЛАВЛЕНИЕ
1.4.6 Множественные реализации
Основные средства, поддерживающие объектно-ориентированное программирование, а именно: производные классы и виртуальные функции,- можно использовать и для поддержки абстракции данных, если допустить несколько реализаций одного типа. Вернемся к примеру со стеком:
template < class T >
class stack
{
public: virtual void push ( T ) = 0; // чистая виртуальная функция
virtual T pop () = 0; // чистая виртуальная функция
};
Обозначение =0 показывает, что для виртуальной функции не требуется никакого определения, а класс stack является абстрактным, т. е. он может использоваться только как базовый класс. Поэтому стеки можно использовать, но не создавать:
class cat { /* ... */ };
stack < cat > s; // ошибка: стек - абстрактный класс
void some_function ( stack <cat> & s, cat kitty ) // нормально
{
s.push ( kitty );
cat c2 = s.pop ();
// ... }
Поскольку интерфейс стека ничего не сообщает о его представлении, от пользователей стека полностью скрыты детали его реализации.
Можно предложить несколько различных реализаций стека. Например, стек может быть массивом:
template < class T >
class astack : public stack < T >
{
// истинное представление объекта типа стек
// в данном случае - это массив
// ... public: astack ( int size );
~astack ();
void push ( T );
T pop ();
};
Можно реализовать стек как связанный список:
template < class T >
class lstack : public stack < T >
{
// ... };
Теперь можно создавать и использовать стеки:
void g ()
{
lstack < cat > s1 ( 100 );
astack < cat > s2 ( 100 );
cat Ginger;
cat Snowball;
some_function ( s1, Ginger );
some_function ( s2, Snowball );
}
О том, как представлять стеки разных видов, должен беспокоиться только тот, кто их создает (т. е. функция g()), а пользователь стека (т. е. автор функции some_function()) полностью огражден от деталей их реализации. Платой за подобную гибкость является то, что все операции над стеками должны быть виртуальными функциями.