C++のクラスの継承について詳しく解説

C++において、クラスの継承は非常に重要な概念です。

継承を使うことで、既存のクラスを拡張したり、新しいクラスを作成することができます。

しかし、初心者にとっては継承の概念が難解であることがあります。

本記事では、C++のクラスの継承について、サンプルコードも含めて解説していきます。

目次

クラスの継承とは

クラスの継承とは、既存のクラスを基にして新しいクラスを作成することです。継承元のクラスを親クラス(または基底クラス)、継承先のクラスを子クラス(または派生クラス)と呼びます。

子クラスは、親クラスが持つメンバ変数やメンバ関数をそのまま利用できます。また、子クラスでは新たにメンバ変数やメンバ関数を追加することもできます。

例えば、以下のようなPerson(人)とStudent(学生)の2つのクラスがある場合、

class Person {
public:
    std::string name;
    int age;
    void sayHello() {
        std::cout << "こんにちは!私は" << name << "です。" << std::endl;
    }
};

class Student : public Person{
};

Studentクラスは Personクラスから派生したものであると考えることができます。この場合、Person クラスが親クラス、Student クラスが子クラスになります。

次に、Student クラスに Person クラスから継承したメンバ変数やメンバ関数を追加してみましょう。

class Student : public Person{
public:
    int grade;
    void rankCheck(){
        sayHello();
        std::cout << name << "さんの階級は" << grade << "です" << std::endl;
    }
};

このようにすることで、Studentクラスでは Personクラスから継承した namesayHello() メソッドを利用しつつ、新たに grade メンバ変数と rankCheck() メソッドを追加することができました。

int main() {
    Student member;
    member.name = "山田太郎";
    member.grade = 4;
    member.rankCheck();

    return 0;
}
こんにちは!私は山田太郎です。
山田太郎さんの階級は4です

以上が C++ の継承機能の基本的な説明です。次に具体的な使い方や注意点について解説していきます。

マルチ継承

マルチ継承とは、複数のクラスから同時に継承することを指します

C++では、複数のクラスを親に持つ子クラスを作成することができます。

以下は、マルチ継承の例です。

#include <iostream>
using namespace std;

class A {
public:
    void printA() {
        cout << "This is class A." << endl;
    }
};

class B {
public:
    void printB() {
        cout << "This is class B." << endl;
    }
};

class C : public A, public B {
public:
    void printC() {
        cout << "This is class C." << endl;
    }
};

int main() {
    C obj;
    obj.printA();
    obj.printB();
    obj.printC();
    
    return 0;
}

この例では、AクラスとBクラスから派生したCクラスを定義しています。そして、Cクラスのオブジェクトを作成し、それぞれのメソッドを呼び出しています。

実行結果は以下のようになります。

This is class A.
This is class B.
This is class C.

このように、マルチ継承を使うことで複数の親クラスから機能を受け継ぐことができます。

ただしマルチ継承は複雑化しやすいため、複雑な階層構造や重複したメソッド名などがある場合は注意が必要です。

継承の利点

継承は、オブジェクト指向プログラミングにおいて非常に重要な概念の一つです。継承を利用することで、コードの再利用性が高まり、ポリモーフィズムを実現することができます。

コードの再利用

継承を利用することで、既存のクラスを拡張した新しいクラスを作成することができます

これにより、同じような機能を持つ複数のクラスを作成する必要がある場合でも、共通部分を親クラスにまとめることができます。このため、コードの重複を避けることができます。

例えば、以下のようなAnimalクラスがあったとします。

class Animal {
public:
    void eat() {
        std::cout << "食べる" << std::endl;
    }
};

このAnimalクラスは、「食べる」という機能を持っています。次に、このAnimalクラスを継承してCatクラスを作成してみましょう。

class Cat: public Animal {
public:
    //何らかの処理
};

int main(){
    Cat cat;
    cat.eat()
}

CatクラスはAnimalクラスから「食べる」という機能を受け継いでいます。

これにより、Catクラスでは「食べる」という機能を再定義することなくeat()関数を呼び出せます。

ポリモーフィズム

ポリモーフィズムは、「多様性」や「多相性」という意味です。C++では、関数やオブジェクトなどの振る舞い(動作)が変化する仕組みです。

継承によって派生した子クラスは親クラスのメンバ関数や変数も引き継ぎます。

そのため子クラスでも親クラスで定義されたメンバ関数や変数にアクセス可能です。しかし子クラスでは親クラスで定義されたメンバ関数や変数だけではなく自身で追加やオーバーライド(メンバ関数の再定義)したメンバ関数や変数も存在します。

例えば以下のようなAnimal Cat Dog クラスがあった場合、

class Animal {
public:
    virtual void speak() { // 仮想関数
        std::cout << "動物" << std::endl;
    }
};

class Cat : public Animal {
public:
    void speak() override { // オーバーライド
        std::cout << "ニャー" << std::endl;
    }
};

class Dog : public Animal {
public:
    void speak() override { // オーバーライド
        std::cout << "ワン" << std::endl;
    }
};

それぞれspeak()メソッド(関数)内部処理は異なります。 しかし以下のように書くことで同じインタフェース(API)から呼び出すことが出来ます。

void animal_speak(Animal& animal) { // 引数animalは参照渡し
    animal.speak();
}

int main() {
    Animal a; 
    Cat c; 
    Dog d;

    animal_speak(a); // 動物 
    animal_speak(c); // ニャー 
    animal_speak(d); // ワン 

   return 0;
}

このようにポリモーフィズムは異なる型でも同じインタフェースから呼び出すことが出来ます。

また上記例ではspeak()メソッド内部処理内容も異なっています。 これらはオブジェック指向プログランミング言語特有の特徴です。

継承の注意点

継承は、C++において非常に重要な概念の一つです。しかし、継承を誤用するとコードの保守性が低下したり、バグの原因となることもあります。そのため、継承を使用する際には注意が必要です。

継承の階層の設計

継承を使用する場合、クラス間の関係性が複雑になることがあります。そのため、継承の階層を適切に設計する必要があります。

例えば、以下のようなクラス構造があった場合、

class Animal {
public:
    void eat();
};

class Dog : public Animal {
public:
    void bark();
};

class Poodle : public Dog {
public:
    void dance();
};

PoodleクラスはDogクラスから継承していますが、Dogクラス自体はAnimalクラスから継承しています。このように多段階で継承された場合、コードの保守性や可読性が低下する可能性があります。

そのため、必要最小限の階層構造で設計することが望ましいです。

継承のアクセス指定子

C++では、継承元クラスから派生先クラスへメンバ変数やメンバ関数を引き継ぐことができます。ただし、アクセス指定子(public, protected, private)によって引き継ぐ範囲を制限することもできます。

例えば、

class Animal {
public:
    void eat();
protected:
    int age;
private:
    std::string name;
};

class Dog : public Animal {
public:
    void bark();
};

class Poodle : public Dog {
public:
    void dance();
};

この場合、DogクラスはAnimalクラスからeat()関数とage変数を引き継ぐことができます。しかし、name変数はprivate指定されているため引き継げません。

継承の仮想関数

C++では仮想関数(virtual function)を使用してポリモーフィズム(多態性)を実現することができます。仮想関数は派生先クラスでオーバーライド(上書き)される可能性があることを明示的に示している関数です。

例えば、

class Animal {
public:
    virtual void eat() { std::cout << "Animal is eating." << std::endl; }
};

class Dog : public Animal {
public:
    virtual void eat() { std::cout << "Dog is eating." << std::endl; }
};

int main() {
    Animal* animal = new Dog();
    animal->eat(); // "Dog is eating." と出力される
}

このように仮想関数を使用することで、同じ型でも異なる動作を行うことができます。ただし、仮想関数は呼び出し時にオーバーヘッド(処理負荷)が発生するため、処理速度が求められる場面では注意が必要です。

終わりに

以上、C++のクラスの継承について、基本的な概念からマルチ継承までを解説しました。

継承はコードの再利用性やポリモーフィズムを実現するために非常に重要な機能ですが、階層の設計やアクセス指定子、仮想関数など注意点もあります。

しっかりと理解して使いこなすことで、より効率的かつ柔軟なプログラミングが可能になるでしょう。

目次