C++マニアック

C++,クラスの継承,class inheritance

ページの末尾へ   トップページ > class inheritance

TOPIC  節電しましょう。電力の使用状況グラフ(東電)

クラスの継承、class inheritance

既にあるクラスの定義を内包するようなクラスを作成することを、クラスの継承といいます。元のクラスをベースクラスとか基本クラスとか親クラスなどと呼び、新しく出来上がったクラスをディライブドクラスとか派生クラスとか子クラスなどと呼びます。クラスを継承すること自体を、サブクラス化とよぶこともあります。これらは、すべて同じことを異なる言葉で表しているだけです。このページでは、元のクラスを基本クラスと呼び、基本クラスを継承してできたクラスを派生クラスと呼ぶことにします。

  1. 最も簡単な例
  2. アクセス制御、public、protected、private
  3. 具体例、Sprintf
  4. 多重継承
  5. 抽象クラス
  6. 抽象クラスの継承
  7. 仮想継承
  8. 継承関連本

最も簡単な例 ページの先頭へ

最も簡単なクラスを、もっとも単純に継承すると、次のようになります。

class CManiac {};   // 最も単純な基本クラスの定義

// 派生クラスの定義
class CManiacInherited : public CManiac {};

青で強調したところが、継承に係わる部分です。クラスの継承をするときには、このように、基本クラスの名前を : public CManiac と書いた部分のようにクラス定義に追加します。これで、基本クラス CManiac から CManiacInherited を派生させた、事になります。

ナオ : でっ、これで、何ができるの?
店主 : まぁまぁ、あわてなさんな。すぐに分かるから。

アクセス制御、public、protected、private ページの先頭へ

ここで、public って何? ということになりますが。これは、派生クラスのユーザに対して、基本クラスのメンバへのアクセスの追加制限を行わないという指定です。つまり、アクセスは、基本クラスで定義されているメンバの public、protedted、private の別に従って行われます。

基本クラスのメンバへのアクセスの追加制限を行う場合には、上記の public に代えて protected または private を使います。このような場合には、派生クラスのユーザに対しては、基本クラスのメンバの public、protected、private の指定と比較して、厳しいほうの制限が行われます。アクセス制限は public < protected < private の順に厳しくなります。このようにして決まったアクセス制限について、派生クラスのユーザは、次の表のようにメンバにアクセスできます。

厳しい方のアクセス制限が 派生クラスのユーザは、基本クラスのメンバに
public の場合 アクセスできる
protedted の場合 アクセスできない
private の場合 アクセスできない

上記は、クラスのユーザに対するアクセス制御でしたが、派生クラスの作者については、基本クラスで定義されているアクセス制御そのものが適用されます。つまり、public、protected、privateのどれを書いても、クラスの作者は基本クラスに対する自分自身のアクセス権を制限することにはならないわけです。基本クラスのアクセス制限 public、protected、private によって、クラスの作者は、次の表に従って、メンバにアクセスできます。

基本クラスのアクセス制限が 派生クラスの作者は、基本クラスのメンバに
public の場合 アクセスできる
protedted の場合 アクセスできる
private の場合 アクセスできない

ちょっと、具体例をみてみましょう。まずクラスの作者の場合、基本クラスの public だけが関係します。

class CManiac {
public:
    int m_iValue;
};

// 派生クラスの定義
class CManiacInherited : protected CManiac {
public:
    void function() {
        int i = m_iValue;   // 派生クラスの作者が、基本クラスのメンバにアクセスする場合、public のみが関係する。
                            // 自分で書いた protected によってアクセスを制限されることは無い。
    }
};

派生クラスのユーザの場合はどうなるかというと、次のように CManiacInherited のオブジェクトからは、protectedpublic の厳しいほうである protected が適用されるので、m_iVaue にはアクセスできません。アクセスできないとは、コンパイルエラーになってしまうという意味です。

int main() {
    CManiacInherited maniacinherited;
    maniacinherited.m_iValue;   // コンパイルエラーになる。ユーザからは protected なメンバは使えない。
}

上記のように、クラスの継承時には、基本クラスのアクセス制御と、派生クラス定義でのアクセス制御の厳しいほうが、クラスのユーザに適用されるというわけです。これによって、派生クラスの作者は、自分には見えているけれども、ユーザには使わせたくない基本クラスのメンバを隠すことができるようになっているわけです。

ナオ : 派生クラスのユーザへのアクセス制御は、厳しい方が適用されるってわけね。
店主 : ですな。次は、ちょっと具体例でも出しましょうかな、お嬢ちゃん。
ナオ : ハイハイっ、そうしてっ。

具体例、Sprintf ページの先頭へ

では、ここで、クラスの継承の具体例を見てみましょう。C 言語や C++ のライブラリには、printf という関数がありますね。この関数は、標準出力に文字列をフォーマットして出力してくれる関数です。sprintf という関数もあって、これは、文字列をメモリに書くようになっています。

C++ マニアック的には、メモリに書いてくれるのではなく、STL の string を返してくれる方が嬉しい、というわけで、そのようなクラス Sprintf を string を基本クラスとして定義してみましょう。

#include <string>
#include <cstdarg>
using namespace std;

struct Sprintf : public string {
    Sprintf(const char* pcFormat, ...) {
        va_list valist;
        va_start(valist, pcFormat);
        char acTable[5000];
        vsprintf(acTable, pcFormat, valist);
        assign(acTable);    // 基本クラス string の関数
        va_end(valist);
    }
};

Sprintf は、簡単に、クラスではなく、構造体(struct)としました。C++ の構造体は、中身がすべて public なクラスであると考えてください。これで、Sprintf は string でもあるので、次のように、標準出力 cout に直接出力することができます。つまり、クラスの継承によって printf と同様のフォーマット機能を持った string が出来上がったというわけです。

#include <iostream>
using namespace std;
int main() {
    cout << Sprintf("How maniac = %d", 1000) << endl;
    return 0;
}

ナオ : マニピュレータの作り方でも同じようなことやってたっしょ。でも、この Sprintf の方がスマートかもね。
店主 : まぁね。ひとつのことをするための方法は、ひとつだけじゃないってことですな。

... to be continued!