Ранее мы реализовали простой стек, элементами которого были указатели void * (см. листинг 6.9). Во-первых, нетипизированные указатели могут быть источником ошибок, так как преобразования в тип void * делаются «молча». В стек, предназначенный, например, для хранения указателей на числа, нечаянно может попасть указатель на строку. Хуже того, в стек может попасть указатель на локальную переменную, которую и уничтожать не требуется. Во-вторых, очевидный недостаток такой универсальности — необходимость явного приведения типа при каждой выборке указателя из контейнера. Мы можем избавиться от явного приведения типа за счет наследования. Например, мы можем организовать стек с указателями на double (листинг 8.9).
Листинг 8.9. Применение наследования для специализации указателей стека class doubleStack: public TStack
{ public:
void push(double *s)
{ TStack::push(s); }
double *top() const
{ return (double *)TStack::top();
double *pop()
{ return (double *)TStack::pop();
}
// положить в стек
// выдать элемент с вершины
// удалить с вершины
Мы упрятали (инкапсуляция — в действии) явное преобразование в методы. Использовать стек столь же просто, и при этом нет нужды выполнять преобразование указателей:
// добавляем
// элементы
// в стек
// без преобразования
// без преобразования
doubleStack t: t.push(new double(ll)); t.push(new double(22)); t.push(new double(33)) while (!t.empty()) { cout << *(t.top()) << endl:
double *p = t.pop():
delete p:
}
Программа выведет на экран
33 22 11
Заменив в листинге 8.9 слово double словом string, получим стек с указателями на строки (листинг 8.10).
Листинг 8.10. Применение наследования для специализации указателей стека
class StringStack: public TStack
{ public:
void push(string *s) { TStack::push(s); }
string *top() const { return (string *)TStack::top(); }
string *pop() { return (string *)TStack::pop(); }
};
Комментарии излишни. Использование такого стека тоже не отличается сложностью:
StringStack s: s.push(new string("lllM)); s.push(new string(M222")): s.push(new string("333")): string *ss:
// без преобразования // без преобразования
while ((ss=s.pop())!=0) { cout << *ss << endl: delete ss:
}
Благодаря наследованию мы можем реализовать стек не указателей, а самих чисел типа double (листинг 8.11).
Листинг 8.11. Реализация стека без указателей class Stackdouble: public TStack
{
public: void push(const double& d) { double *p = new double(d); TStack::push(p);
}
double top() const
{ double *p = (double *)TStack::top(); return *p;
}
double pop()
// организовали указатель // поместили указатель
{ double double delete
return t:
(double *)TStack::pop();
// получили указатель
// сохранили значение
// возвратили память
// возврат значения
}
};
Мы упрятали внутрь методов не только преобразование, но и указатели. Теперь метод push () сам организует указатель, помещаемый в стек, а метод рор() возвращает память системе. Работа с таким стеком для пользователя стала проще:
Stackdouble t: t.push(ll/2.0): t.push(22); t.push(33): while (It.emptyO) { double p = t.pop(); cout << p << endl:
}
Пользователь этого стека и не подозревает, что работает с указателями. |