3.5. Оператори за увеличаване и намаляване с
1
Операторите за увеличаване (“++”) и намаляване (“—“) с единица
предлагат един удобен, компактен запис за добавяне и изваждане на 1 от
променлива. За двата оператора може да се мисли като за оператори за
присвояване; операндите трябва да бъдат стойности за запис (lvalue). Всеки
оператор има както префксна, така и постфиксна форма. Например,
main()
{
int c; ++c; // prefix increment
c++; // postfix increment }
Нека
да илюстрираме използването на пост- и префиксната форма на операторите като
дефинираме стеков клас IntStack, който поддържа цели стойности. За IntStack са
дефинирани две операции:
1. push(int v), която поставя стойността на v на
върха на стека.
2. pop(), която връща стойността, записана на върха на стека.
Освен това възможни са два особени случая:
1. препълване: опит за запис в
стека, когато е пълен.
2. изпразване: опит за четене от стека, когато той е
празен.
IntStack ще бъде представен чрез един масив от цели числа.
Следователно, необходими са следните членове (елементи):
int size;
int
*ia;
int top;
top винаги ще съдържа индекса на елемента на върха на
стека. Това означава, че за празен стек top има стойност -1. Стекът е пълен
когато top има стойност size-1.
Написването на функциите isEmpty() и
isFull(), които връщат стойности истина или лъжа е елементарно:
typedef
int Boolean;
extern const int BOS; // Bottom of Stack
Boolean
IntStack::isEmpty() { return (top == BOS);}
Boolean IntStack::isFull() {
return (top == size-1);}
Реализацията на push() илюстрира префиксната
форма на оператора за увеличение с 1. Да припомним, че top сочи текущия връх на
стека. Новият елемент трябва да бъде поставен в елемент с едно по-голям от
текущата стойност на top:
void IntStack::push( int v ) { // add v to the
top of stack
if ( isFull() ) grow(); // enlarge stack
ia[ ++top ] = v;
}
Префиксната форма на ++ увеличава стойността на top преди тази стойност
да бъде използувана като индекс в ia. Това е компактния запис на следните два
оператора:
top = top + 1;
ia[ top ] = v;
grow() увеличава
размера на стека с някакъв предварително зададен брой елементи. Раздел 5.1
съдържа реализацията на grow().
Реализирането на pop() илюстрира
постфиксната форма на оператора за намаляване с 1. Припомняме отново, че top
сочи текущия връх на стека. След като бъде извлечен елемент от стека, top трябва
да бъде намален с 1.
int IntStack::pop() {
// return the topmost
element of stack
if( isEmpty() ) // report error and exit() ;
return ia[
top-- ]; }
Постфиксната форма на—намалява стойността на top след като
тази стойност е била използувана като индекс на ia. Това е компактен запис на
следната двойка оператори:
ia[ top ];
top = top - 1;
След
намаляването на top се изпълнява оператора return.
Това,което остава за
цялостното дефиниране на IntStack, е управлението на стека му. Понеже вътрешното
представяне на нашия стеков клас е същото както и на дефинирания по-рано клас
IntArray, ние ще използуваме реализацията на IntArray като базова за IntStack
(вж. Раздел 2.8 ) за дефиницията на класа IntArray):
#include
"IntArray.h"
typedef int Boolean;
const int BOS = -1; // Bottom of
Stack
class IntStack :
private IntArray
public: IntStack( int sz =
ARRAY_SIZE ) : IntArray( sz ),
top( BOS ) {} Boolean isEmpty();
Boolean
isFull();
void push( int );
int pop();
void grow() {}
protected: int
top;
}
При описанието на IntArrayRC, IntArray беше деклариран като
базов клас public. При описанието на IntStack, IntArray е деклариран като базов
клас private. Първата разлика между базовите класове private и public е, че на
базов клас private не могат да бъдат присвоявани обекти от произлезлия клас.
Например,
extern swap( IntArray&, int, int );
IntArray ia;
IntArrayRC ia1;
IntStack is; ia = ia1; // ok: public base class
ia = is;
// error: private base class
swap( ia1, 4, 7 ); // ok: public base
class
swap( is, 4, 7 ); // error: private base class
Втората разлика
между базовите класове private и public е, че наследените членове на базовия
клас private се разглеждат като собствени членове на произлезлия клас. Това
означава, че интерфейсът public на класа IntArray не е достъпен през класовите
обекти на IntStack. Например,
int bottom = is[0]; // error: operator[]
private
Наследствеността при класовете може да бъде използувана по два
основни начина:
1. Като метод за създаване на подтип на съществуващ клас,
както е при дефинирането на IntArrayRC, произлязъл от класа IntArray, описан в
глава 1.
2. Като метод за повторно използуване на дадена реализация с цел
създаване на нов тип клас, както е при дефинирането на IntStack, произлязъл от
класа IntArray.
IntStack не е подтип на IntArray. Той не разделя
операциите, които могат да бъдат прилагани към обектите на класа IntArray. Чрез
определянето на IntArray като базов клас private на IntStack не се допуска
случайното прилагане на операциите на IntArray върху обекти на класа IntStack.
Освен това, ако един обект от класа IntStack случайно бъде присвоен на обект от
класа IntArray целостта на стека може сериозно да бъде повредена.
Предотвратяването на такива инциденти е втората причина за дефинирането на
IntArray като базов клас private на IntStack. Един базов клас private не може да
бъде присвояван на класов обект от произлезлия му клас. Ето един пример за
обекти от клас IntStack:
#include <stream.h>
#include
"IntStack.h"
IntStack myStaack;
const DEPTH = 7;
main() {
for (
int i = 0; i < DEPTH; ++i ) myStack.push(i);
for ( i = 0; i < DEPTH;
++i ) cout << myStack.pop() << "t";
cout << "n";
}
Когато тази програма бъде компилирана и изпълнена, тя връща следния
резултат:
6 5 4 3 2 1 0
Упражнение 3-3. Как мислите, защо С++ не е
наречен ++С?