C++ Learning

第47回 確認問題(継承と多態)

STEP 7 の 継承・仮想関数・vtable・抽象クラス・多重継承 の総合確認クイズ 12 問。

進め方

スコア 0 / 12 回答済み 0 継承 virtual vtable 抽象 多重
継承 継承の基本 0 / 2

Q1 継承時の基底クラスのコンストラクタ呼び出し順は?

派生 → 基底
基底 → 派生
同時
プログラマが選ぶ
基底 → 派生の順。派生クラスは基底のメンバを使えるので、先に基底が初期化されます。破棄は逆順。

Q2 Animal a = dog; で起きるのは?

コンパイルエラー
参照が自動で取られる
スライシング(派生部分が切り落とされる)
多態性が働く
派生を基底の値型に代入するとスライシング。多態性を活かすには参照・ポインタ・unique_ptr を使う必要があります。
virtual 仮想関数 0 / 3

Q3 virtual なしの関数を基底ポインタから呼ぶとどうなる?

派生の実装が呼ばれる
基底の実装が呼ばれる(静的バインディング)
コンパイルエラー
実行時エラー
virtual なしだと静的バインディング。ポインタの型(基底)で呼び出し先が決まるので、基底の実装が呼ばれます。多態が必要なら virtual を付ける。

Q4 override を付ける最大のメリットは?

実行速度が上がる
タイポ/シグネチャ違いをコンパイル時に検出できる
必須のキーワード
派生クラスが final になる
override は「基底に virtual 関数がある」ことを保証。タイポ・const 付け忘れ等の事故を防ぎます。

Q5 仮想関数を持つクラスのデストラクタを virtual にしないと何が起きる?

特に問題ない
仮想関数も動かなくなる
基底ポインタから delete で派生の dtor が呼ばれず未定義動作
コンパイルエラー
virtual なしだと基底の dtor しか呼ばれず、派生部分のリソースリーク。基底クラスには必ず virtual ~Base() = default;
vtable vtable 0 / 2

Q6 仮想関数を持つクラスのオブジェクトに追加されるのは?

vtable 本体
参照カウンタ
vptr(vtable へのポインタ)
型名の文字列
オブジェクトごとに vptr が追加。vtable 自体はクラスごとに 1 つ。

Q7 基底コンストラクタ内で仮想関数を呼ぶと?

派生の実装が呼ばれる
基底の実装が呼ばれる(vptr はまだ基底の vtable を指している)
コンパイルエラー
null ポインタ参照
コンストラクタは基底 → 派生の順で動き、その途中では vptr は基底の vtable を指しています。そのため基底 ctor 内で仮想呼び出しすると基底の版が呼ばれる。落とし穴のひとつ。
抽象 抽象クラス 0 / 3

Q8 純粋仮想関数の正しい宣言は?

virtual void f();
abstract void f();
virtual void f() = 0;
void f() override = 0;
C++ は末尾に = 0 を付ける特殊構文。abstract キーワードは存在しません。

Q9 抽象クラスに対してできることは?

インスタンス化
関数を直接呼ぶ
ポインタ・参照として使う(派生を指す)
何もできない
抽象クラスは直接インスタンス化できませんが、ポインタ・参照の型としては使え、派生を指すのが普通の使い方。

Q10 純粋仮想関数をすべて override しない派生クラスの状態は?

具象クラス(インスタンス化可)
抽象クラスのまま(インスタンス化不可)
コンパイルエラー
実行時エラー
未実装が残っていれば派生も抽象。さらに派生して全部実装した時点で初めて具象に。
多重 多重継承 0 / 2

Q11 菱形継承の問題を解決する方法は?

派生クラスで再定義
中間クラスで virtual 継承を使う
final を使う
解決不可
class B : virtual public A で virtual 継承すると、A のインスタンスが 1 つに統合されます。

Q12 C++ で多重継承を安全に使う推奨パターンは?

深い継承階層
全基底を virtual 継承
多重継承は禁止
単一継承 + 純粋インターフェース(データメンバ無し)の多重実装
データメンバを持たない純粋インターフェースなら菱形問題が起きません。「extends 1 つ + implements 複数」の Java 風スタイル。

お疲れさまでした 🎉

0 / 12
次へ: STEP 8 テンプレート