next up previous
: 被保護メンバの使用 : 継承 : 継承

クラスの継承

スタックを表すクラス stackclass があるとする. その public な関数に pushpop があり,


#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 のオブジェクトに対して pushpop を行うと, スタックが空か一杯かを調べて, スタックポインタが配列の範囲を逸脱するときにはそれに対する 処理を行うことになる.


stackclass s1;
safestack s2;

...
  s1.push(10); /* チェックしない */
  s2.push(20); /* チェックを行う */
  ...
  x = s2.pop();  /* チェックを行う */
  y = s1.pop();  /* チェックしない */

このように,定義済のクラスを少し変更したクラスを定義するとき, 基本的に定義済のクラスの定義を用い, 変更部分だけを定義しなおすのが楽である.

上の例の class safestack: public stackclass の部分の指示により, stackclass の公開メンバで safestack で再定義していないメンバは, そのまま stackclass に引き継ぐことを指示する. これを 継承という. メンバ関数 pushpop は 定義し直しているため,safestack のオブジェクトに対して, pushpop を行えば, 新たに定義したメンバ関数を実行するが, fullempty 及びコンストラクタは再定義していないため, 基本クラス stackclass で定義したのものを使用する. コンストラクタやデストラクタも派生クラスで再定義しても良い. 派生クラスで新たなメンバ関数を追加してもよい.

元のクラス stackclass の変数 sparea[] は,非公開メンバであるので, 新たに safestack のメンバ関数を定義するときには 使用できない.

既にあるクラスの定義を利用した 新しいクラスを定義するとき, クラス名の直後に アクセス指定子を書く. 上の safestack の定義では public と アクセス指定子を指定している. アクセス指定子は, 元になるクラスのメンバについて 公開するかどうかの性質を どう引き継ぐかを指定する. 指定可能なアクセス指定子には,public の他に,privateprotected がある. これらを指定すると次のような意味になる.

アクセス指定子 アクセス制御
public 基本クラスのすべての公開メンバを派生クラスの公開メンバとする.
private 基本クラスのすべての公開メンバは派生クラスの非公開メンバとする.
protected 基本クラスの被保護メンバは派生クラスのメンバからアクセス可能

このとき, 基本クラスとは元になるクラスを意味し, 派生クラスとは,基本クラスの定義を引き継いで新たに定義するクラスを いう.

新たにメンバ関数の種類を増やしたりするときなど, 継承を利用すると便利である.

問題 3-1

整数型のデータを格納するスタックのクラス stack1 を定義せよ. メンバ関数として pushpopemptyfull を持つこと. このとき,スタックのオーバフロー, アンダーフローに対する処理は行わなくてもよい. スタックポインタの変数は非公開メンバとする.

さらに,指定した個数の要素をスタックから削除する メンバ関数 multipop を持つ新たなクラス stack2 を, stack1 の派生クラスとして定義せよ. ただし,multipop は,


void multipop(int n); /* スタックに n 個以上の要素が積まれていれば,
                         スタックから n 個の要素を取り去る.
                         n 個未満の場合は,スタックに積まれている
                         要素すべてを取り去る */
であるとする. 継承する際のアクセス指定子には public を使用せよ.



Takeshi Kumagai 平成19年6月19日