情報処理 II − 第13回

講義に入る前に shori2 にカレントディレクトリを移しておいてください。


1.継承


既に定義されているクラスに データ(メンバ)や機能(メソッド)を追加して、 別のクラスを定義することができます。 このようにして新しいクラスを定義することを 継承といいます。

例えば、直線上の「現在位置」を表現するクラス position を以下のように定義したとします。

position.h
class position {
  double p;                              // プライベートメンバ(現在位置)
public:
  position(void);                        // デフォルトコンストラクタ
  position(const position &);            // コピーコンストラクタ
  position(double);                      // コンストラクタ(初期位置)
  ~position(void);                       // デストラクタ
  void shift(double);                    // インタフェース関数
  double where(void);                    // インタフェース関数
};

position.cc
#include "position.h"

// デフォルトコンストラクタ
position::position(void) {
  p = 0.0;
}

//コピーコンストラクタ
position::position(const position &p) {
  position::p = p.p;
}

// コンストラクタ(初期位置)
position::position(double p) {
  position::p = p;
}

// デストラクタ
position::~position(void) {
  // とりあえず何もすることはない
}

// インタフェース関数(移動)
void position::shift(double s) {
  position::p += s;
}

// インタフェース関数(現在位置の取り出し)
double position::where(void) {
  return p;
}

このクラス position のプライベートメンバ x および y(現在位置)に値を設定するには、絶対位置を初期値として与えるか、 メンバ関数 shift() を使用して相対位置を与えます。

pmain.cc
#include <iostream.h>
#include "position.h"

int main(void) {
  position p1(10);  // 初期位置は 10
  position p2 = p1;

  p2.shift(30);

  cout << "現在位置は " << p1.where() << endl;
  cout << "現在位置は " << p2.where() << endl;

  return 0;
}

% CC pmain.cc position.cc[Enter]
pmain.cc:
position.cc:
% a.out[Enter]
現在位置は 10
現在位置は 40

このクラスを継承して、 「速度」と「時間」によって現在位置を設定するクラス velocity を定義してみます。

velocity.h
#include "position.h"

class velocity : public position {       // velocity は position を継承
  double v;                              // プライベートメンバ(速度)
public:
  velocity(void);                        // デフォルトコンストラクタ
  velocity(const velocity &);            // コピーコンストラクタ
  velocity(double, double);              // コンストラクタ(初期位置と初速度)
  ~velocity(void);                       // デストラクタ
  void speed(double);                    // 速度変化
  void time(double);                     // 経過時間
};

velocity.cc
#include "velocity.h"

// デフォルトコンストラクタ
velocity::velocity(void) {
  v = 0.0;
}

// コピーコンストラクタ…………基底クラスのコンストラクタの指定↓
velocity::velocity(const velocity &v) : position(v) {
  velocity::v = v.v;
}

// コンストラクタ…………………基底クラスのコンストラクタの指定↓
velocity::velocity(double p, double v) : position(p) {
  velocity::v = v;
}

// デストラクタ
velocity::~velocity(void) {
  // とりあえず何もすることはない
}

// 速度変化の設定
void velocity::speed(double v) {
  velocity::v += v;
}

// 経過時間の設定
void velocity::time(double t) {
  shift(v * t);
}

クラス定義において class velocity : public position { ... のように : public を使ってもとになるクラスを指定することで、 そのクラスを継承した新しいクラスを定義できます。 このもとのクラスを基底クラス、 新しく定義したクラスを派生クラスあるいは 導出クラスと呼びます。

派生クラスのクラス変数を生成する際には、 先に基底クラスのコンストラクタが実行され、 そのあと派生クラスのコンストラクタが実行されます。 実行する基底クラスのコンストラクタを指定するには、 コンストラクタの定義(実体)の関数名の後ろに :(コロン)を置き、 それに続けて実行する基底クラスのコンストラクタと引数を書きます。 これを指定しなければデフォルトコンストラクタが実行されます。

一方、派生クラスのクラス変数を消去する際は、 生成するときとは逆に、派生クラスのデストラクタを実行した後、 基底クラスのデストラクタが呼び出されます。

vmain.cc
#include <iostream.h>
#include "velocity.h"

int main(void) {
  velocity v1(20, 10);  // 初期位置 20、初速度 10
  velocity v2 = v1;

  v1.time(10);   // v1 は10秒経過
  v2.shift(30);  // v2 は位置を30移動

  cout << "現在位置は " << v1.where() << endl;
  cout << "現在位置は " << v2.where() << endl;

  return 0;
}

% CC vmain.cc velocity.cc position.cc[Enter]
vmain.cc:
velocity.cc:
position.cc:
% a.out[Enter]
現在位置は 120
現在位置は 50

上の例で使用している speed(), time() はいずれも velocity クラスのメソッドですが、 shift() や where() は velocity 型ではなく、その基底クラスの position のメソッドです。 すなわち、派生クラスのクラス変数に対して 基底クラスの public(あるいは protected)メンバを適用することができます。


課題38


初速度 v0 から加速度 at 秒間加速すると、 速度は v = at + v0 になり、 その間に s = at2 / 2 + v0t 進みます。 この加速度 a と加速時間 t を与えて現在位置を設定するクラス acceleration を、上で定義したクラス velocity を継承して定義してください。

amain.cc
#include <iostream.h>
#include "acceleration.h"

int main(void) {
  acceleration a(0, 10, -1); // 初期位置 0、初速度 10、加速度 -1
  int h;

  while ((h = a.where()) >= 0) {
    for (int i = 0; i < h; i++) {
      cout << ' ';
    }
    cout << "*" << endl;
    a.time(1);
  }
  return 0;
}

% a.out[Enter]
*
         *
                  *
                         *
                                *
                                     *
                                          *
                                             *
                                                *
                                                 *
                                                  *
                                                 *
                                                *
                                             *
                                          *
                                     *
                                *
                         *
                  *
         *
*

作成したソースプログラムをメールに添付して、 tokoi まで送ってください。Subject:(件名)は kadai38 としてください。