ホーム > 製品情報 > SophiaFramework UNIVERSE > チュートリアル > C++ の基礎 > 継承

C++の基礎 : C++ の基本知識

継承


クラスには継承と呼ばれるメカニズムがあり、これにより既存のクラスの再利用性が高まります。継承を使うと次のようなことが実現できます。

ここでは、継承による機能追加について説明します。継承により既存クラスの挙動をカスタマイズするには、継承とともに仮想関数と呼ばれる機構を使用します。仮想関数については後章で解説します。

クラスの継承による機能追加

ここに鉛筆があるとします。鉛筆には芯があり、書くたびにどんどん小さくなっていきます。これをクラスにしてみましょう。

class Pencil
{
private:
    SIntN core;      // 鉛筆の芯の長さ
public:
    Void Write()
    {
        core -= 1;   // 書くたびに芯が 1mm ずつ小さくなる
        if (core < 0) core = 0;
    }
};

あるとき、鉛筆の後ろに消しゴムをくっつけた新製品が売り出されました。消しゴム付き鉛筆です。消しゴム付き鉛筆は、鉛筆と同じ機能をもちながら、さらに消しゴムの機能ももっています。

このように機能が追加されたクラスは次のように定義することができます。

class ErasePencil : public Pencil
{
private:
    SIntN rubber;    // 消しゴムの大きさ
public:
    Void Erase()
    {
        rubber -= 1;  // 消すたびにゴムが 1 立方mm ずつ小さくなる
        if (rubber < 0) rubber = 0;
    }
};

class キーワードに : public Pencil を指定することで、Pencil のメンバ変数とメンバ関数を引継ぐことができます。つまり、ErasePencil はメンバ変数 core とメンバ関数 Write() をもっています。それに加えて、新しく定義したメンバ変数 rubber とメンバ関数 Erase() ももっています。したがって、次のようなコードを書くことができます。

ErasePencil pen;
pen.Write();       // Pencil クラスから引き継いだメンバ関数の呼び出し
pen.Erase();       // ErasePencil クラスで新たに定義したメンバ関数の呼び出し

このように、既存のクラスのメンバを引き継ぐことを継承といいます。引き継ぎの元となるクラスを基底クラス、引き継いで新しく定義したクラスのことを派生クラスといいます。「ErasePencil は Pencil の派生クラスである」とか「Pencil は ErasePencil の基底クラスである」という言い方をします。

protected メンバ

鉛筆付き消しゴムはさらに改良されました。消しゴムで消すということは、一度書くのに使われた鉛筆の芯が無駄になるということ。だから、消しゴムで消し取った鉛筆の芯を再利用すれば、芯の減り方が少なくて済むではないか。

こうして新しい製品が開発されました。これは「伸びる鉛筆」という名前で売り出されました。鉛筆の後ろにくっついている消しゴムで消すと、消し取った芯が鉛筆の芯に追加されて、どんどん長い鉛筆になっていくのです!

この伸びる鉛筆をクラスにすると、次のようになります。

class GrowPencil : public Pencil
{
private:
    SIntN rubber;    // 消しゴムの大きさ
public:
    Void Erase()
    {
        rubber -= 1;  // 消すたびにゴムが 1 立方mm ずつ小さくなる
        if (rubber < 0) rubber = 0;
        
        core += 1;    // 消すたびに芯が 1mm ずつ伸びていく
    }
};

しかし、このクラスは正しく動作しません (コンパイル エラーになります)。なぜなら、メンバ変数 core は Pencil クラスで定義されており、GrowPencil がそのメンバ変数を引き継いではいますが、core は private メンバ変数なので、Pencil クラスの外部からアクセスできないのです。

このような問題を解消するために、protected アクセス制限があります。private メンバはそのメンバを宣言したクラスからしかアクセスできませんが、protected メンバはそのメンバを宣言したクラスだけでなく、その派生クラスからもアクセスできます。

つまり、Pencil クラスを次のように定義しておけば、上記の GrowPencil クラスの定義はエラーになりません。

class Pencil
{
protected:
    SIntN core;      // 鉛筆の芯の長さ
    ...
};

派生クラスのオブジェクトの型

ErasePencil クラスはPencil クラスのメンバをすべて引き継いでいますから、ErapsePencil のオブジェクトを Pencil のオブジェクトとして扱うことができます。

Void WriteWithPencil(Pencil pen)
{
    pen.Write();
}

Void MyFunction()
{
    ErasePencil epen;
    GrowPencil gpen;

    WriteWithPencil(epen);
    WriteWithPencil(gpen);
}

このように、「派生クラスのオブジェクトは基底クラスのオブジェクトでもある」ということができます。上記の例ですと、「epenオブジェクトは ErasePencil 型であると同時に Pencil 型でもある」ということができます。

この章のまとめ

やってみよう

次のようなコードを実行してみましょう!

Write() メンバ関数が引き継がれていることを確認しましょう。Pencil クラスの core メンバ変数を privateにするとどうなるでしょうか? どんなエラーがでるでしょうか?

Void TestInherit(Void)
{
    Pencil pen;
    ErasePencil epen;
    GrowPencil gpen;

    pen.Write();
    epen.Write();
    epen.Erase();
    gpen.Write();
    gpen.Erase();
}