#include
class iStack {
public:
iStack( int capacity )
: _stack( capacity ), _top( 0 ) { }
bool pop( int &top_value );
bool push( int value );
bool full();
bool empty();
void display();
int size();
private:
int _top;
vector _stack;
iStack выглядит следующим образом:
};
Стек реализован на основе вектора из элементов типа int. При создании объекта класса iStack его конструктор создает вектор из int, размер которого (максимальное число элементов, хранящихся в стеке) задается с помощью начального значения. Например, следующая инструкция создает объект myStack, который способен содержать не более 20 элементов типа int:
iStack myStack(20);
При манипуляциях с объектом myStack могут возникнуть две ошибки:
* запрашивается операция pop(), но стек пуст;
* запрашивается операция push(), но стек полон.
Вызвавшую функцию нужно уведомить об этих ошибках посредством исключений. С чего же начать?
Во-первых, мы должны определить, какие именно исключения могут быть возбуждены. В C++ они чаще всего реализуются с помощью классов. Хотя в полном объеме классы будут представлены в главе 13, мы все же определим здесь два из них, чтобы использовать их как исключения для класса iStack. Эти определения мы поместим в заголовочный файл stackExcp.h:
// stackExcp.h
class popOnEmpty { /* ... */ };
class pushOnFull { /* ... */ };
В главе 19 исключения в виде классов обсуждаются более подробно, там же рассматривается иерархия таких классов, предоставляемая стандартной библиотекой C++.
Затем надо изменить определения функций-членов pop() и push() так, чтобы они возбуждали эти исключения. Для этого предназначена инструкция throw, которая во многих отношениях напоминает return. Она состоит из ключевого слова throw, за которым следует выражение того же типа, что и тип возбуждаемого исключения. Как выглядит инструкция throw для функции pop()? Попробуем такой вариант:
// увы, это не совсем правильно
throw popOnEmpty;
К сожалению, так нельзя. Исключение – это объект, и функция pop() должна генерировать объект класса соответствующего типа. Выражение в инструкции throw не может быть просто типом. Для создания нужного объекта необходимо вызвать конструктор класса. Инструкция throw для функции pop() будет выглядеть так:
// инструкция является вызовом конструктора
throw popOnEmpty();
Эта инструкция создает объект исключения типа popOnEmpty.
Напомним, что функции-члены pop() и push() были определены как возвращающие значение типа booclass="underline" true означало, что операция завершилась успешно, а false – что произошла ошибка. Поскольку теперь для извещения о неудаче pop() и push() используют исключения, возвращать значение необязательно. Поэтому мы будем считать, что эти функции-члены имеют тип void:
class iStack {
public:
// ...
// больше не возвращают значения
void pop( int &value );
void push( int value );
private:
// ...
};
Теперь функции, пользующиеся нашим классом iStack, будут предполагать, что все хорошо, если только не возбуждено исключение; им больше не надо проверять возвращенное значение, чтобы узнать, как завершилась операция. В двух следующих разделах мы покажем, как определить функцию для обработки исключений, а сейчас представим новые реализации функций-членов pop() и push() класса iStack:
#include "stackExcp.h"
void iStack::pop( int &top_value )
{
if ( empty() )
throw popOnEmpty();
top_value = _stack[ --_top ];
cout "iStack::pop(): "top_value " endl;
}
void iStack::push( int value )
{
cout "iStack::push( " value " )\n";
if ( full() )
throw pushOnFull( value );
_stack[ _top++ ] = value;
}
Хотя исключения чаще всего представляют собой объекты типа класса, инструкция throw может генерировать объекты любого типа. Например, функция mathFunc() в следующем примере возбуждает исключение в виде объекта-перечисления . Это корректный код C++:
enum EHstate { noErr, zeroOp, negativeOp, severeError };
int mathFunc( int i ) {
if ( i == 0 )
throw zeroOp; // исключение в виде объекта-перечисления
// в противном случае продолжается нормальная обработка
}
Упражнение 11.1