classD : publicB { };
// is-a 関係// B の public → D でも public// B の protected → D でも protected
private 継承特殊用途のみ
classD : privateB { };
// is-implemented-in-terms-of 関係// B の public → D では private// 外から D を B として扱えない
原則:ほぼ常に public 継承。private/protected 継承は「実装の再利用のためだけ」に継承するケースで使いますが、ほとんどの場合合成(メンバ変数として持つ)のほうが良い設計。
4. メモリレイアウト
派生オブジェクトは、メモリ上で「基底部分 + 派生部分」の順に並びます。
Dog のメモリレイアウト(概念):
[Animal 部分]std::string name_ (継承)
[Dog 独自部分]int age_
このレイアウトのおかげで:
Dog を Animal として扱える(先頭部分が Animal のメモリレイアウトと同じ)
Animal* p = &dog; としても p->name_ にアクセス可能
これが次章(仮想関数)のアップキャストの基礎
スライシング問題
派生オブジェクトを基底の「値型」に代入すると、派生部分が切り落とされます(スライシング)。
スライシング落とし穴
Dog d{"ポチ"};
Animal a = d; // ← Dog 部分が切り落とされる!// a は Animal なので、Dog の bark() は使えない// 多態を使いたいときは参照・ポインタ・スマートポインタで持つAnimal& ref = d; // OK: Dog のまま扱えるAnimal* p = &d; // OKstd::unique_ptr<Animal> up = std::make_unique<Dog>("ポチ"); // OK
モダン C++ の原則: 多態性(次章)を活かしたいなら、派生オブジェクトは必ず参照・ポインタ・スマートポインタで扱う。値のコンテナ std::vector<Animal> は Dog を入れるとスライシングで壊れます。std::vector<std::unique_ptr<Animal>> が正解。
⭐
ここまでで継承の基本は OK
最後の §5 は設計思想。継承を使うか合成を使うかの判断は、現代 C++ でとても重要。
5. is-a vs has-a(継承 vs 合成)
「新クラスに既存クラスの機能を取り込みたい」ときの 2 つの手段:
継承(is-a)厳選
classDog : publicAnimal { };
// Dog is-a Animal// Dog は Animal として使える
合成(has-a)推奨
classCar {
Engine engine_; // ← メンバとして持つWheel wheels_[4];
};
// Car has-a Engine// Car は Engine として使えない(当然)
判断フロー
「X は Y の一種」と日本語で言える → 継承(is-a)
「X は Y を持つ / 使う」 → 合成(has-a)
迷ったら合成を選ぶ(変更が局所的、テストしやすい、依存が緩い)
典型的な誤用:
「内部で Vector のような処理が欲しい」→ class MyClass : public std::vector<T> にしない。std::vector をメンバに持つ
「Player に HP と MP と Inventory を持たせたい」→ それぞれを継承しない。メンバとして持つ
継承は「本当に is-a 関係が成立する」場合だけ。 疑わしきは合成です。
Scott Meyers の格言:「継承よりも合成を優先せよ (Prefer composition over inheritance)」― Effective C++ の基本原則のひとつ。
広告スペース
確認クイズ
継承の基本を 4 問で確認。
Q1. class Dog : public Animal { }; のとき、Dog から見える Animal の private メンバは?