C++継承

継承(Inheritance)について

C++の継承とは

継承とは、一つのオブジェクトが他のオブジェクトの性質を獲得するプロセスです。

C++では、あるクラスの性質を受け継がせたクラスを作ることができ、これによって継承の機能を実現しています。

親になるクラスを「基底クラス(スーパークラス)」、子になるクラスを「派生クラス(サブクラス)」と呼びます。

なお、継承機能のように、プログラマが派生クラスに必要な部分のみをコーディングし、効率的な開発をすることを「差分プログラミング」といいます。


継承の定義方法

派生クラスを作成するには、クラスの宣言時に以下の構文を使用します


class 派生クラス名 : アクセス識別子 基底クラス{
    // メンバ
};

アクセス識別子は以下の通りです。

指定 説明
public 基底クラスのメソッドやデータは公開メンバとなり、派生クラスのオブジェクトを用いて外部からアクセスできます。
private 基底クラスのメソッドやデータは私的メンバとなり、派生クラスのオブジェクトを用いて外部からアクセスすることはできません。ただし、基本クラスの公開メンバへのアクセスは派生クラスからは可能です。
protect 基底クラスのメソッドやデータは限定的な公開メンバとなり、派生クラスからアクセスできますが、派生クラス以外に対してはprivateと同様になります。メンバへのアクセス権は、あくまで基本クラスか派生クラスのみが保有します。

コンストラクタとデストラクタの継承について

コンストラクタとデストラクタは継承されません。

ただし、派生クラスのオブジェクトを生成した際には、基底クラスのコンストラクタが呼び出されます。呼び出し順は「クラスの派生順(基底→派生→派生...)」です。

また、デストラクタも各クラスのものがそれぞれ呼び出されます。デストラクタはコンストラクタとは逆順(派生→基底)で呼び出されます。

基底クラスのコンストラクタがオーバーロードしている場合には、引数を初期化リストに明示するなどの対応が必要です。


継承の動作サンプルプログラム

public継承

public識別子は、「基底クラスのpublicメンバを派生クラスでもpublicにする」という意味になります。


#include <iostream>

class BaseClass{
public:
    void base_print(){ printf("base class\n"); }
};

class DerivedClass : public BaseClass{
public:
    void print(){ printf("derived class\n"); }
};

int main(void)
{
    DerivedClass obj;
    obj.print();      // 派生クラスのメソッド
    obj.base_print(); // 基底クラスのメソッド
    return(0);
}

private継承

private識別子を使用した場合、派生クラスのオブジェクトから基底クラスのメソッドを呼び出すことはできません。

基底クラスのメソッドを呼び出すには派生クラス内から呼び出すこととなります。


#include <iostream>

class BaseClass{
public:
    void base_print(){ printf("base class\n"); }
};

class DerivedClass : private BaseClass{
public:
    void print(){
        printf("derived class\n");
        BaseClass::base_print(); // 基底クラスのメソッド呼び出し
    }
};

int main(void)
{
    DerivedClass obj;
    obj.print();          // 派生クラスのメソッド
    // obj.base_print();  // 基底クラスのメソッド呼び出しはエラーとなります。
    return(0);
}

protected継承

protected識別子を使用した場合、基底クラスのメソッドやデータは限定的な公開メンバとなります。

メンバに対して、派生クラスからアクセスできますが、派生クラス以外に対してはprivateと同様になります。

メンバへのアクセス権は、あくまで基本クラスか派生クラスのみが保有します。


// 基底クラス
class base{
public:
    int val_public;
protected:
    int val_protected;
private:
    int val_private;
};

// 派生クラス
class derived : protected base{
};

// さらに派生するクラス
class derived2 : public derived{
public:
    void func()
    {
        // derived(このクラスの基本クラス)メンバへのアクセス
        val_public    = 4;
        val_protected = 5;
        // val_private = 6;  // エラー
    }
};

int main()
{
    // derived クラス外部からのアクセス
    derived2 obj;
    obj.func();

    //obj.val_public    = 1;  // エラー
    //obj.val_protected = 2;  // エラー
    //obj.val_private   = 3;  // エラー

    return(0);
}

継承時のスコープ解決について

メンバ名の衝突

派生クラスと基底クラスのメンバ名が同名で定義された場合には、派生クラスは常に派生クラス(自分)のメンバを優先します。

派生クラスから基底クラスの同名メンバを呼び出すには、スコープ解決演算子を用いてクラスを明示します


#include <iostream>

class BaseClass{
public:
    void print(){ printf("base class\n"); }
};

class DerivedClass : public BaseClass{
public:
    void print(){ printf("derived class\n"); }
};

int main(void)
{
    DerivedClass obj;
    obj.print();            // 派生クラスのメソッド
    obj.BaseClass::print(); // 基底クラスのメソッド
    return(0);
}

関連ページ