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." と出力される
}

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

1 2

この記事のページ一覧
  1. 現在のページ
目次から探す