スタックを表すクラス stackclass
があるとする.
その public な関数に push
と pop
があり,
#define MAXSIZE 100 class stackclass { public: int sp; /* stack pointer */ int area[MAXSIZE]; stackclass(void) { sp = 0;} // constructor to initialize sp void push(int); int pop(void); bool empty(void); bool full(void); }; int stackclass::pop(void) { return area[--sp]; } void stackclass::push(int item) { area[sp++] = item; }であるとする. スタックに何も積まれていない状態で
pop
が呼ばれると,
配列の領域を逸脱することになる.
また,push
でも同様にスタックが溢れるかどうかを調べていないとする.
(注意)
area[sp++] = item;
は,現在の sp
の値を使って area[sp] = item
を
実行した後 sp
の値を 1 増やす.
また,
return area[--sp];
は,sp
の値を 1 減らしてから,area[sp]
の値を求める.
このとき,スタックのオーバフローやアンダーフローが起きると
そのことを知らせてくれる,
より安全なスタックのクラスを定義することを考える.
そのクラス名を safestack
とすると,次のように定義できる.
class safestack: public stackclass { public: void push(int); int pop(void); }; void safestack::push(int item) { if (!full()) area[sp++] = item; else { /* スタック溢れの処理 */ } } int safestack::pop(void) { if (!empty()) return area[--sp]; else { /* スタックアンダーフローの処理 */ } }
クラス safestack
のオブジェクトに対して
push
や pop
を行うと,
スタックが空か一杯かを調べて,
スタックポインタが配列の範囲を逸脱するときにはそれに対する
処理を行うことになる.
stackclass s1; safestack s2; ... s1.push(10); /* チェックしない */ s2.push(20); /* チェックを行う */ ... x = s2.pop(); /* チェックを行う */ y = s1.pop(); /* チェックしない */
このように,定義済のクラスを少し変更したクラスを定義するとき, 基本的に定義済のクラスの定義を用い, 変更部分だけを定義しなおすのが楽である.
上の例の class safestack: public stackclass
の部分の指示により,
stackclass
の公開メンバで safestack
で再定義していないメンバは,
そのまま stackclass
に引き継ぐことを指示する.
これを 継承という.
メンバ関数 push
と pop
は
定義し直しているため,safestack
のオブジェクトに対して,
push
や pop
を行えば,
新たに定義したメンバ関数を実行するが,
full
,empty
及びコンストラクタは再定義していないため,
基本クラス stackclass
で定義したのものを使用する.
コンストラクタやデストラクタも派生クラスで再定義しても良い.
派生クラスで新たなメンバ関数を追加してもよい.
元のクラス stackclass
の変数 sp
と
area[]
は,非公開メンバであるので,
新たに safestack
のメンバ関数を定義するときには
使用できない.
既にあるクラスの定義を利用した
新しいクラスを定義するとき,
クラス名の直後に
アクセス指定子を書く.
上の safestack
の定義では public
と
アクセス指定子を指定している.
アクセス指定子は,
元になるクラスのメンバについて
公開するかどうかの性質を
どう引き継ぐかを指定する.
指定可能なアクセス指定子には,public
の他に,private
,
protected
がある.
これらを指定すると次のような意味になる.
アクセス指定子 | アクセス制御 |
public |
基本クラスのすべての公開メンバを派生クラスの公開メンバとする. |
private |
基本クラスのすべての公開メンバは派生クラスの非公開メンバとする. |
protected |
基本クラスの被保護メンバは派生クラスのメンバからアクセス可能 |
このとき, 基本クラスとは元になるクラスを意味し, 派生クラスとは,基本クラスの定義を引き継いで新たに定義するクラスを いう.
新たにメンバ関数の種類を増やしたりするときなど, 継承を利用すると便利である.
問題 3-1
整数型のデータを格納するスタックのクラス stack1
を定義せよ.
メンバ関数として push
,pop
,empty
,full
を持つこと.
このとき,スタックのオーバフロー,
アンダーフローに対する処理は行わなくてもよい.
スタックポインタの変数は非公開メンバとする.
さらに,指定した個数の要素をスタックから削除する
メンバ関数 multipop
を持つ新たなクラス stack2
を,
stack1
の派生クラスとして定義せよ.
ただし,multipop
は,
void multipop(int n); /* スタックに n 個以上の要素が積まれていれば, スタックから n 個の要素を取り去る. n 個未満の場合は,スタックに積まれている 要素すべてを取り去る */であるとする. 継承する際のアクセス指定子には
public
を使用せよ.