Самый простой способ ослабить связь между пользователем класса и его создателем, а также между программами, в которых объекты создаются, и программами, в которых они используются, состоит в введении понятия абстрактных базовых классов. Эти классы представляют интерфейс со множеством реализаций одного понятия. Рассмотрим класс set, содержащий множество объектов типа T:
class set { public: virtual void insert(T*) = 0; virtual void remove(T*) = 0;
virtual int is_member(T*) = 0;
virtual T* first() = 0; virtual T* next() = 0;
virtual ~set() { } };
Этот класс определяет интерфейс с произвольным множеством (set), опираясь на встроенное понятие итерации по элементам множества. Здесь типично отсутствие конструктора и наличие виртуального деструктора, см. также §6.7. Рассмотрим пример:
class slist_set : public set, private slist { slink* current_elem; public: void insert(T*); void remove(T*);
int is_member(T*);
virtual T* first(); virtual T* next();
slist_set() : slist(), current_elem(0) { } };
class vector_set : public set, private vector { int current_index; public: void insert(T*); void remove(T*);
int is_member(T*);
T* first() { current_index = 0; return next(); } T* next();
vector_set(int initial_size) : array(initial_size), current_index(0) { } };
Реализация конкретного типа используется как частный базовый класс, а не член класса. Это сделано и для удобства записи, и потому, что некоторые конкретные типы могут иметь защищенный интерфейс с целью предоставить более прямой доступ к своим членам из производных классов. Кроме того, подобным образом в реализации могут использоваться некоторые классы, которые имеют виртуальные функции и не являются конкретными типами. Только с помощью образования производных классов можно в новом классе изящно переопределить (подавить) виртуальную функцию класса реализации. Интерфейс определяется абстрактным классом.
Теперь пользователь может записать свои функции из §13.2 таким образом:
void my(set& s) { for (T* p = s.first(); p; p = s.next()) { // мой код } // ... }