高度なOOP
継承の先にある、実践的な設計の細部を整える。
多重継承
1つのクラスに複数の特性を組み合わせたいときに使います。 便利ですが、設計が複雑になりやすいので、用途を絞るのが重要です。
#include <iostream>
class Flyable {
public:
virtual void fly() { std::cout << "飛行中" << std::endl; }
virtual ~Flyable() = default;
};
class Swimmable {
public:
virtual void swim() { std::cout << "泳ぎ中" << std::endl; }
virtual ~Swimmable() = default;
};
class Duck : public Flyable, public Swimmable {
public:
void quack() { std::cout << "クワッ" << std::endl; }
};
int main() {
Duck duck;
duck.fly();
duck.swim();
duck.quack();
return 0;
}
飛行中
泳ぎ中
クワッ
仮想デストラクタ
基底クラスを介して派生クラスを破棄する可能性があるなら、 仮想デストラクタは必須です。
#include <iostream>
#include <memory>
class Base {
public:
virtual ~Base() {
std::cout << "Base破棄" << std::endl;
}
};
class Derived : public Base {
public:
~Derived() override {
std::cout << "Derived破棄" << std::endl;
}
};
int main() {
{
std::unique_ptr<Base> ptr = std::make_unique<Derived>();
}
return 0;
}
Derived破棄
Base破棄
委譲とコンポジション
継承だけではなく、委譲で機能をまとめると柔軟性が上がります。
#include <iostream>
#include <string>
class Printer {
public:
void print(const std::string& text) const {
std::cout << text << std::endl;
}
};
class ReportService {
private:
Printer printer;
public:
void outputReport(const std::string& title) {
printer.print("Report: " + title);
}
};
int main() {
ReportService service;
service.outputReport("売上レポート");
return 0;
}
Report: 売上レポート
型キャストと安全性
ダウンキャストは便利ですが、必ず安全性を確認してから使います。
#include <iostream>
#include <memory>
class Shape {
public:
virtual ~Shape() = default;
};
class Circle : public Shape {
public:
void roll() {
std::cout << "転がる" << std::endl;
}
};
int main() {
std::unique_ptr<Shape> shape = std::make_unique<Circle>();
if (auto circle = dynamic_cast<Circle*>(shape.get())) {
circle->roll();
}
return 0;
}
転がる
pImpl イディオム
実装詳細を隠したいときは、pImpl のような手法で依存を減らせます。 公開ヘッダを小さく保てるのが利点です。
よくある誤り
誤り1: 継承で無理に状態共有しすぎる
誤り2: virtual なしで基底ポインタを delete する
誤り3: dynamic_cast の結果確認を省く
ポイント
- 多重継承は局所的に使うと便利
- 仮想デストラクタは破棄の安全性に直結する
- 継承と委譲を使い分けると設計が安定する
- dynamic_cast は安全確認付きで使う
- pImpl は実装隠蔽に向く
やってみよう
練習1: Flyable と Swimmable を組み合わせた別の動物を作る
練習2: Base ポインタ経由で delete して破棄順を確認する
練習3: pImpl を使った軽いクラス設計を考える
まとめ
- 高度なOOPでは継承だけでなく委譲も重要
- 安全な破棄と安全なキャストが実践で効く
- Phase 9 では STL を設計面から扱う