В главе 6 мы затронули проблему универсальности контейнеров, работа которых фактически не зависит от типа элементов. Открытое наследование и виртуальные деструкторы позволяют нам решить проблему универсализации по-другому. Идея состоит в последовательном применении полиморфизма: нужно определить единый базовый класс, а все остальные классы сделать наследниками от него. Тогда контейнер разрабатывается под элементы базового класса, однако принцип подстановки позволяет использовать его и с элементами производных классов. Базовый класс в простейшем виде может содержать только виртуальный деструктор, например:
class TObject { public:
virtual -TObjectO = 0;
};
inline TObject::~TObject() {}
Тогда контейнер-стек может быть реализован так, чтобы хранить указатели на TObject (листинг 9.11).
Листинг 9.11. Стек для хранения объектов базового класса
// узел списка
// информационная часть
// указательная часть
// конструктор узла
class TStack { struct Elem { Object *data; Elem *next;
Elem (TObject *d, Elem *p) :data(d), next(p) { }
// вершина стека
// закрыли копирование
// закрыли присваивание
// пустой стек
// поместить элемент в стек
// получить элемент с вершины стека
}
// удалить элемент из стека
// если стек пустой - ничего не делать
// сохранили для возврата
// запомнили указатель
// передвинули вершину
// возвратили память
// возвратили элемент
// стек пустой, если указатель = 0
}:
Elem * Head; TStack(const TStack &); TStack& operator=(const TStack &); public:
TStack(): Head(0) {} void push(TObject *d) { Head = new Elem(d, Head); } void *top() const { return emptyO? в: Head->data; TObject *pop() { if (emptyO) return 0; Object *top = Head->data; Elem *oldHead = Head; Head = Head->next; delete oldHead; return top;
}
bool emptyO const { return Head==0; }
}:
В таком варианте в контейнер не попадут «случайные» указатели, как это было возможно в прежнем варианте с нетипизированными указателями. Наследуя от такого стека, можно реализовать стек для любого типа, унаследовавшего от TObject.
Такой подход не только имеет право на жизнь, но и достаточно широко применяется. В «самом объектно-ориентированном» языке программирования Smalltalk все классы считаются наследниками единственного базового класса, определенного разработчиками языка. Из современных языков, в которых реализована та же идея, можно отметить Java и С#. Интересно, что в этих языках отсутствуют средства работы с динамической памятью.
В С++ такого единого базового класса нет, однако было создано немало библиотек, построенных по этому принципу. Самые известные из них — библиотека MFC (Microsoft Foundation classes) [46], в которой реализован единый базовый класс COb j ect, и библиотека VCL (Visual Component Library) [49, 54], в которой основным базовым классом является TObject. |