C++で学ぶ継承の全技術!初心者向け7つのサンプルコードで解説

C++での継承を徹底解説するイメージC++
この記事は約14分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

この記事を読むことで、あなたはC++の基本的な概念である継承について、その使い方から応用技術までを深く理解し、実際にプログラムを書く際にこの知識を活用できるようになります。

継承はオブジェクト指向プログラミングの重要な側面であり、コードの再利用性を高め、より効率的で読みやすいプログラムを作成するために不可欠です。

この記事では、初心者でも理解しやすい言葉を使いながら、C++における継承の概念を段階的に解説していきます。

●C++とは

C++は、システムプログラミングからゲーム開発、デスクトップアプリケーション開発に至るまで、幅広い分野で利用されている汎用プログラミング言語です。

C言語の拡張として開発され、オブジェクト指向プログラミングの機能を備えています。

C++の特徴は、高いパフォーマンスと柔軟性にあります。

直接ハードウェアレベルの操作が可能でありながら、オブジェクト指向の概念を用いてより高度なプログラミングが行える点が魅力です。

○C++の基本的な特徴

C++は、オブジェクト指向プログラミングをサポートすることで最もよく知られています。

オブジェクト指向プログラミングでは、データとそれを操作する関数を組み合わせた「オブジェクト」という単位でプログラムを構築します。

これにより、プログラムのモジュール性、再利用性、メンテナンス性が向上します。

また、C++は低レベルのメモリ管理を行うことができるため、システムプログラミングや組込みシステム開発にも広く使われています。

○プログラミング初心者にとってのC++

プログラミング初心者にとって、C++は学習の際にいくつかの挑戦が伴いますが、その分、プログラミングの基本的な概念と実践的なスキルを身につけることができます。

オブジェクト指向プログラミングの概念を理解し、適切にクラスや継承を使用することで、より洗練されたコードを書くことが可能になります。

また、C++を学ぶことで、メモリ管理やポインタなど、他の高レベル言語では抽象化されている低レベルの概念についても深く理解することができます。

●継承の基本

C++での継承は、一つのクラス(親クラスと呼ばれます)の特性を別のクラス(子クラスと呼ばれます)に引き継ぐことを指します。

これにより、コードの再利用性が高まり、プログラムの構造がより明確になります。

継承はオブジェクト指向プログラミングの基本概念の一つであり、クラス間の関係性を表現する強力な手段となります。

親クラスで定義されたメソッドやプロパティは、子クラスでも自動的に使用することができ、必要に応じて子クラスでこれらを拡張または変更することが可能です。

○継承とは何か

継承とは、あるクラス(基底クラスまたはスーパークラスとも呼ばれる)の機能を別のクラス(派生クラスまたはサブクラスとも呼ばれる)が引き継ぐプログラミングの概念です。

これにより、派生クラスは基底クラスのメソッドや変数を継承し、追加や改良を加えることができます。

継承はコードの重複を減らし、効率的なプログラム開発を促進します。

また、継承を利用することで、既存のコードを基に新しい機能を容易に追加することが可能になり、ソフトウェアの柔軟性と保守性が向上します。

○継承のメリットと基本構造

継承のメリットは多岐にわたります。

最も重要なのは、コードの再利用性の向上です。

親クラスで定義されたメソッドや変数を、子クラスが継承することで、同じコードを何度も書く必要がなくなります。

これにより、開発時間の短縮とプログラムの信頼性の向上が期待できます。

また、継承はプログラムの階層構造を明確にし、オブジェクト間の関係を容易に理解できるようにします。

基本構造では、親クラスの特性が子クラスに引き継がれ、子クラスはそれらを拡張またはオーバーライド(上書き)することができます。

これにより、より柔軟かつ効率的なプログラム開発が可能になります。

●継承の使い方

C++における継承の使い方を理解するためには、基本的な概念として、クラス間の関係性を明確にすることが重要です。

継承を使用することで、既存のクラス(親クラスまたは基底クラス)の属性や振る舞いを、新しいクラス(子クラスまたは派生クラス)に引き継ぐことができます。

子クラスは親クラスの特性を継承しながら、新たな特性を加えることが可能です。

この過程では、既存のコードを再利用し、より効率的なプログラム開発が行えます。

○サンプルコード1:基本的な継承の実装

基本的な継承の実装では、親クラスにある属性やメソッドを子クラスが継承します。

例えば、車を表すクラス(Carクラス)から、特定の車種を表すクラス(例えば、Sedanクラス)を派生させる場合を考えます。

// 親クラスの定義
class Car {
public:
    Car() { cout << "Carのコンストラクタが呼び出されました" << endl; }
    void drive() { cout << "車が走ります" << endl; }
};

// 子クラスの定義
class Sedan : public Car {
public:
    Sedan() { cout << "Sedanのコンストラクタが呼び出されました" << endl; }
    void park() { cout << "セダンが駐車します" << endl; }
};

int main() {
    Sedan myCar;
    myCar.drive(); // 親クラスのメソッドを呼び出す
    myCar.park();  // 子クラスで定義されたメソッドを呼び出す
}

この例では、SedanクラスがCarクラスから継承を受けています。

Sedanクラスは、Carクラスのdriveメソッドを継承し、新たにparkメソッドを追加しています。

このように継承を使用することで、コードの重複を避けつつ、新たな機能を追加できます。

○サンプルコード2:コンストラクタとデストラクタの継承

継承では、コンストラクタとデストラクタも重要な役割を果たします。

子クラスのオブジェクトが作成される際、まず親クラスのコンストラクタが呼び出され、次に子クラスのコンストラクタが呼び出されます。

オブジェクトの破棄時には、この逆の順序でデストラクタが呼び出されます。

// 親クラスの定義
class Parent {
public:
    Parent() { cout << "Parentのコンストラクタ" << endl; }
    ~Parent() { cout << "Parentのデストラクタ" << endl; }
};

// 子クラスの定義
class Child : public Parent {
public:
    Child() { cout << "Childのコンストラクタ" << endl; }
    ~Child() { cout << "Childのデストラクタ" << endl; }
};

int main() {
    Child myObject; // これにより、コンストラクタとデストラクタが呼び出される
}

この例では、Childクラスのインスタンスが生成される際、まずParentクラスのコンストラクタが実行され、次にChildクラスのコンストラクタが実行されます。

オブジェクトが破棄される際には、Childのデストラクタが先に実行され、その後Parentのデストラクタが実行されます。

これにより、オブジェクトの初期化と清掃が適切に行われます。

●継承の応用

C++における継承の応用は、より複雑なプログラミングシナリオでの利用を想定しています。

多重継承の利用、抽象クラスの使用、オーバーライドとポリモーフィズムの活用などが含まれます。

これらの応用的な技術を駆使することで、柔軟かつ効率的なソフトウェア設計が可能になります。

多重継承では、複数の親クラスから属性や振る舞いを継承することができ、抽象クラスでは具体的な実装を子クラスに委ねることができます。

また、オーバーライドとポリモーフィズムを使用することで、同じインターフェイスを持つが異なる動作をするメソッドを実装することが可能です。

○サンプルコード3:多重継承の利用

多重継承を用いると、複数のクラスから特性を継承することができます。

例えば、飛行可能な車を表すクラスを定義する場合、CarクラスとPlaneクラスの両方から特性を継承することが考えられます。

// 基底クラス1
class Car {
public:
    void drive() { cout << "車が走ります" << endl; }
};

// 基底クラス2
class Plane {
public:
    void fly() { cout << "飛行機が飛びます" << endl; }
};

// 多重継承を使用した派生クラス
class FlyingCar : public Car, public Plane {
};

int main() {
    FlyingCar myFlyingCar;
    myFlyingCar.drive(); // Carクラスから継承
    myFlyingCar.fly();   // Planeクラスから継承
}

このコード例では、FlyingCarクラスはCarPlaneの両方の機能を継承しています。

これにより、FlyingCarのインスタンスはdriveメソッドとflyメソッドの両方を使用できます。

○サンプルコード4:抽象クラスと継承

抽象クラスは、一部または全部のメソッドが実装されていないクラスです。

これを使用すると、派生クラスでこれらのメソッドを具体的に実装する必要があります。

// 抽象クラス
class Shape {
public:
    virtual void draw() = 0; // 純粋仮想関数(抽象メソッド)
};

// 抽象クラスを継承した派生クラス
class Circle : public Shape {
public:
    void draw() override { cout << "円を描きます" << endl; }
};

int main() {
    Circle circle;
    circle.draw(); // "円を描きます"と出力
}

この例では、Shapeクラスは抽象クラスであり、drawメソッドの実装は派生クラスであるCircleに委ねられています。

○サンプルコード5:オーバーライドとポリモーフィズム

オーバーライドとポリモーフィズムを利用することで、同じメソッド名でも異なるクラスで異なる振る舞いを実現できます。

// 基底クラス
class Animal {
public:
    virtual void speak() { cout << "動物が鳴きます" << endl; }
};

// 派生クラス1
class Dog : public Animal {
public:
    void speak() override { cout << "犬が吠えます" << endl; }
};

// 派生クラス2
class Cat : public Animal {
public:
    void speak() override { cout << "猫が鳴きます" << endl; }
};

int main() {
    Dog dog;
    Cat cat;
    dog.speak(); // "犬が吠えます"と出力
    cat.speak(); // "猫が鳴きます"と出力
}

この例では、AnimalクラスのspeakメソッドがDogクラスとCatクラスでオーバーライドされています。これにより、異なる動物の鳴き声を表現することができます。

このように、オーバーライドとポリモーフィズムは、同一のインターフェイスに対して異なる実装を提供する強力な手段です。

●注意点と対処法

C++での継承を用いる際には、いくつかの重要な注意点があります。

これらの注意点を理解し、適切に対応することで、より効果的かつ安全なプログラムを作成することができます。

継承を使用する際には特に、アクセス制御の問題、多重継承によるダイヤモンド問題、そして基底クラスと派生クラス間の緊密な結合に注意する必要があります。

アクセス制御は、基底クラスのメンバがどのように派生クラスからアクセスされるかを定義するものです。

公開(public)、保護(protected)、非公開(private)の各キーワードを適切に使用することで、クラスのカプセル化を保持し、不要なアクセスから保護することが可能です。

多重継承におけるダイヤモンド問題は、二つの基底クラスが同じ祖先を持つ場合に発生することがあります。

この問題を解決する一つの方法は、仮想継承を使用することです。

また、基底クラスと派生クラス間の緊密な結合は、将来的な変更を困難にする可能性があるため、クラス間の独立性を保つことが重要です。

○継承を使う際の一般的な落とし穴

継承を使用する際には、継承の概念とその適用に関する理解が必要です。

基底クラスの非公開メンバに派生クラスからアクセスしようとすると、アクセス違反が発生します。

また、複数の基底クラスが同じメソッドを持っている場合、派生クラスでどのメソッドを使用するか明確にする必要があります。

これらの問題を避けるためには、基底クラスの設計を慎重に行い、派生クラスでのメソッドの使用を明確にすることが重要です。

○サンプルコード6:継承におけるアクセス制御

継承におけるアクセス制御の例を紹介します。

この例では、基底クラスBaseにプライベート(private)、プロテクテッド(protected)、パブリック(public)の各メンバがあり、派生クラスDerivedからこれらのメンバへのアクセスを試みます。

class Base {
private:
    int privateVar; // プライベート変数
protected:
    int protectedVar; // プロテクテッド変数
public:
    int publicVar; // パブリック変数

    Base() : privateVar(0), protectedVar(0), publicVar(0) {}
};

class Derived : public Base {
public:
    Derived() {
        // privateVarはアクセス不可能
        // protectedVarはアクセス可能
        protectedVar = 10;
        // publicVarもアクセス可能
        publicVar = 20;
    }
};

int main() {
    Derived obj;
    // obj.privateVarにはアクセス不可能
    // obj.protectedVarにも外部からはアクセス不可能
    // obj.publicVarにはアクセス可能
    obj.publicVar = 30;
}

このコードでは、派生クラスDerivedは、基底クラスBaseのプライベート変数にはアクセスできませんが、プロテクテッド変数とパブリック変数にはアクセスできます。

これにより、基底クラスの内部実装を適切にカプセル化し、不要なアクセスを防ぐことができます。

●継承のカスタマイズ方法

C++における継承のカスタマイズは、プログラムの設計において重要な役割を果たします。

継承のカスタマイズを行うことで、既存のクラスの振る舞いを変更したり、新しい機能を追加したりすることが可能です。

継承のカスタマイズには、基底クラスのメソッドを派生クラスでオーバーライドすることや、派生クラスに新しい属性やメソッドを追加することなどが含まれます。

これにより、プログラムの柔軟性と再利用性が向上し、より効率的なコードの記述が可能になります。

継承のカスタマイズを行う際には、基底クラスと派生クラスの関係を適切に理解し、適切なアクセス修飾子を使用してクラスのカプセル化を保つことが重要です。

○サンプルコード7:継承関係をカスタマイズする方法

継承関係のカスタマイズを表す一例として、基底クラスのメソッドを派生クラスでオーバーライドする方法を紹介します。

この例では、基底クラスVehicleに定義されたmoveメソッドを、派生クラスCarでカスタマイズしています。

// 基底クラス
class Vehicle {
public:
    virtual void move() {
        cout << "乗り物が移動します" << endl;
    }
};

// 派生クラス
class Car : public Vehicle {
public:
    void move() override {
        cout << "車が走ります" << endl;
    }
};

int main() {
    Car myCar;
    myCar.move(); // "車が走ります"と出力される
}

このコードでは、CarクラスはVehicleクラスからmoveメソッドを継承していますが、Car固有の振る舞いを実装するためにこのメソッドをオーバーライドしています。

これにより、CarクラスのオブジェクトはVehicleクラスとは異なる動作を示すようになります。

このようなカスタマイズにより、基底クラスの設計を変更することなく、派生クラスごとに特有の振る舞いを実装することができます。

まとめ

C++における継承は、プログラムの効率性と柔軟性を高めるための強力なツールです。

基本から応用まで、様々な継承の技術を理解し適用することで、再利用可能でメンテナンスしやすいコードを作成することが可能です。

本記事では、初心者にも理解しやすい形で継承の基本、使い方、応用例、注意点、そしてカスタマイズ方法を具体的なサンプルコードとともに解説しました。

これらの知識を活用することで、より効果的なプログラミングが可能になります。