「C++言語」の「継承」の仕組みを身に付けよう!

これまで「クラス」の作り方や使い方について学んできましたが、今回はクラスに「親子関係」を作ることができる「継承」について勉強していきます。

ですが、「継承」については以前に少し学んだことがあって、難しくて理解ができなかったので、ちょっとずつ学んでいきたいと思います。

まだこの先に学ぶ内容がわからないのですが、たぶん「C++言語学習」では、ここが「山場」になるような気がしています。

「クラスの親子関係」を作る「継承」の仕組みとは?

これまで学んできたクラスはそれぞれが「独立」して存在しているため、何も関係性がありませんでした。

クラスに「親子関係」を作ることで、「プログラムの重複を減らす」ことができ、「プログラムの変更も行いやすくなる」という利点があるみたいなので、さっそく「継承」について学んでいきましょう。

今回は、「自転車(Bicycle)」を元にさまざまなクラスの作成に取り組んでみたいと思います。

他にも、「電動自転車(ElectricBicycle)」や、「マウンテンバイク(MountainBike)」クラスを作ってみたいと思います。

これらのクラスに「親子関係」を作ることで、「継承の仕組み」を作っていきます。

まず、「親(基底)」となる「Bicycle」クラスを作っていきたいと思いますが、どのような内容にするかをいろいろと考えていました。

「基底クラス」を作る

これから作るクラスの関係は、

クラス構成

のようになります。

「Bicycle」クラスが持っている「メンバ」と「メンバ関数」は、

speed
自転車の速さ
getSpeed()
「自転車の速さ」を取得するゲッター
forward()
前へ進む
brake()
減速する

のようにしたいと思います。

「Bicycle」クラスを作ってみると、まず、「ヘッダーファイル」は、

#ifndef ___CLASS_Bicycle
#define ___CLASS_Bicycle

class Bicycle {
protected:
    int speed; // 現在の速度
public:
    Bicycle(); // コンストラクタ
    int getSpeed(); // 現在の速度を取得するゲッター
    void forward(int increaseSpeed); // 前へ進む
    void brake(); // ブレーキを掛ける
};

#endif

そして、「実装ファイル」は次のようになります。

#include <iostream>
#include "Bicycle.h"

// コンストラクタ
Bicycle::Bicycle(){
    speed = 0;
}

// 現在の速度を取得するゲッター
int Bicycle::getSpeed(){
    return speed;
}

// 前へ進む
void Bicycle::forward(int increaseSpeed){
    speed += increaseSpeed;
}

// ブレーキを掛ける
void Bicycle::brake(){
    if( speed > 0){
        speed -= 1;
    }
}

もっといろいろな機能を実装できそうですが、自分の頭では把握ができなくなりそうなので、今回はかなりシンプルな構成のクラスを作りました。

このクラスは「継承される元となるクラス」なのですが、「C++言語」では「基底クラス」と呼ばれているそうです。

「派生クラス」を作る

継承される「ElectricBicycle」クラスの「ヘッダーファイル」は、

#ifndef ___CLASS_ElectricBicycle
#define ___CLASS_ElectricBicycle

#include "Bicycle.h"

class ElectricBicycle : public Bicycle {
public:
    void motorAssistForward(); //「モーターアシスト」を使った前進
};

#endif

のようになります。

電動自転車なので、「モーターアシストを使った前進機能」を作成してみました。

このクラスの「実装ファイル」は、

#include <iostream>
#include "ElectricBicycle.h"

//「モーターアシスト」を使った前進
void ElectricBicycle::motorAssistForward(){
    forward(3);
}

のように作ってみました。

そして、「MountainBike」クラスの「ヘッダーファイル」は、

#ifndef ___CLASS_MountainBike
#define ___CLASS_MountainBike

#include "ElectricBicycle.h"

class MountainBike : public Bicycle {
public:
    void gearForward(); // 変速ギアを利用して前進
};

#endif

のようになり、マウンテンバイクなので、「変速ギアを利用した前進機能」を作ってみました。

「実装ファイル」の内容は、

#include <iostream>
#include "MountainBike.h"

// 変速ギアを利用して前進
void MountainBike::gearForward(){
    forward(5);
}

のようになります。

このように「継承されるクラス」のことは「派生クラス」と呼ぶそうです。

「継承関係」の作り方

「C++言語」の継承関係には「3つ」の種類があり、

  • private
  • proctected
  • public

の3つの継承によって、「基底クラス」のメンバの公開範囲が異なるとのことでした。

まず、クラスに「継承関係」を作るためには、

class 継承するクラス : 継承の種類 基底クラス {
    // メンバ
}

のように「ヘッダーファイル」に書くことで、継承関係を作ることができます。

「private」の場合は、「派生クラス」から「基底クラス」の非公開のメンバにはアクセスができないとのことでした。

これはなんとなくわかるかも。

「proctected」の場合は、「基底クラス」の「public」「protected」は「派生クラス」では、「protected」として扱われるとのことでした。

「public」の場合は、「基底クラス」の「private」メンバ以外は、「派生クラス」でも「基底クラスと同様の扱い」になるとのことでした。

メンバの扱い方が継承方法で変化するところがなんか複雑ですね・・・

そもそも、「継承」を使う理由は、「基底クラス(親クラス)」の「メンバ・メンバ関数」を「派生クラス」でも使えるようにすることにあります。

これまでに作ったクラスを実行する「main」関数を作って実行してみると、

#include <iostream>
#include <string>
#include "ElectricBicycle.h"
#include "MountainBike.h"

using namespace std;

int main() {
    // 「ElectricBicycle」クラス
    ElectricBicycle eb;

    eb.motorAssistForward();

    cout << "「ElectricBicycle」のスピード=" << eb.getSpeed() << endl;


    // 「MountainBike」クラス
    MountainBike mb;

    mb.gearForward();

    cout << "「MountainBike」のスピード=" << mb.getSpeed() << endl;

    return 0;
}

のようになります。

このプログラムを実行すると、

「ElectricBicycle」のスピード=3
「MountainBike」のスピード=5

のように表示されます。

「motorAssistForward」関数と「gearForward」関数の中では、「基底クラス」の「Bicycle」クラスで定義されている「forward」関数を使って、「speed」の値を増やしています。

クラス構成2

このように「派生クラス」で「基底クラス」のメンバを利用することができるのが「継承」の仕組みです。

まだなんとなくしか理解できていないところもありますが、これまでより「継承」の考え方が理解できてきたような気がします。

でも、積極的にどんどん使っていくことはまだ難しいので、さらにいろいろなクラスを作り、クラスの使い方を身に付けていきたいと思います。

→(前へ)「C言語」の「デストラクタ・インライン関数・静的メンバ」を勉強しよう!

→(次へ)「C++言語」の仮想関数について理解しよう!

「C++言語」学習書籍

HOMEへ