第36回 確認問題(ライフサイクル)
STEP 5 のサイト最大の山場、ライフサイクル編の総合確認クイズ。全 12 問、カテゴリ別採点。
進め方
- 全問終えると下に総合結果が出ます。9 問以上正解で STEP 6(スマートポインタ編)へ進める状態
- ムーブ・RAII・Rule of Zero はモダン C++ の背骨なので、苦手カテゴリは必ず復習しましょう
ctor
コンストラクタ
0 / 2
Q1 コンストラクタが持てないのは?
引数
戻り値の型
オーバーロード
デフォルト引数
戻り値の型は書きません(オブジェクト自身が実質の戻り値)。引数・オーバーロード・デフォルト引数は可能。
Q2 次のメンバ変数で、初期化子リストが必須なのは?
int x_;
std::string s_;
std::vector<int> v_;
const int MAX_;
const は代入不可なので初期化子リストが唯一の初期化手段。参照メンバ(T&)、デフォルトコンストラクタを持たないクラス型メンバも同様。
dtor/RAII
デストラクタと RAII
0 / 3
Q3 デストラクタが呼ばれるのはいつ?
プログラム終了時のみ
明示的に delete した時のみ
スコープを抜けた時や delete した時(オブジェクトが破棄される瞬間)
GC が動いた時
C++ にはガベージコレクタがなく、オブジェクトはスコープ終了(スタック)や delete(ヒープ)で決定的に破棄されます。この予測可能性が RAII の基礎。
Q4 次の順序で作られた 3 つのオブジェクトの破棄順序は?
{ Obj a; Obj b; Obj c; }
a → b → c
同時
c → b → a(逆順)
b → a → c
スコープ内の変数は作成と逆順(LIFO)で破棄されます。後から作ったものが先に壊れる。依存関係を逆順に解消できる設計。
Q5 RAII の中心的な考え方は?
メモリをヒープに取る
例外を投げない
資源取得をコンストラクタ、解放をデストラクタに
クラスを継承可能にする
RAII = Resource Acquisition Is Initialization。資源の取得と解放をオブジェクトの生存期間に結び付けることで、例外安全性と後始末忘れの防止を同時に達成。
copy
コピー
0 / 2
Q6 Foo b = a; で呼ばれるのは?
コピー代入演算子
コピーコンストラクタ
ムーブコンストラクタ
通常のコンストラクタ
新規変数 b の初期化なのでコピーコンストラクタ。既存の b に b = a; の形ならコピー代入。左辺が既存か新規かで区別。
Q7 生ポインタで動的メモリを持つクラスに、コピーコンストラクタを自分で書かないとどうなる?
コンパイルエラー
深いコピーが自動で生成される
暗黙版が浅いコピーを行う(両者が同じヒープを指す → 二重解放)
実行時にエラーになる
暗黙のコピーコンストラクタはメンバを 1 つずつコピーするだけなので、ポインタ値だけがコピーされます。両者が同じヒープを指し、二重解放で未定義動作。Rule of Zero(STL 型でラップ)が最良の回避策。
move
ムーブ
0 / 3
Q8 std::move(x) は実際に何をする?
x の中身を消す
x のコピーを作る
x を右辺値参照にキャストするだけ(実際の移動はしない)
x のアドレスを返す
std::move は実は何も動かさず、型を右辺値参照にキャストするだけ。実際の移動は受け取り側(ムーブコンストラクタなど)が行います。
Q9 ムーブコンストラクタに付けるべき修飾子は?
const
virtual
noexcept
inline
noexcept を付けないと、std::vector の再確保時にムーブではなくコピーにフォールバックされます。性能のために必須。
Q10 ムーブされた後のオブジェクトについて正しいのは?
もう存在しない
元の値をコピーで保持している
有効だが未定義の状態(STL 型なら通常は空)
自動で破棄される
有効だが未定義。アクセスは合法だが値に期待してはいけない。再代入するか、スコープ終了で破棄されるのを待ちます。
Rule
Rule of 0/3/5 と総合
0 / 2
Q11 次のコードでムーブコンストラクタが暗黙生成されないのは?
class Foo { int x; };
class Foo { ~Foo(){} };
class Foo { std::vector<int> v; };
class Foo { };
デストラクタを自分で書くと、ムーブ 2 つが暗黙生成されなくなります。= default で明示的に復活させる必要があります。これが Rule of Zero を推奨する最大の理由。
Q12 次のコードで発生するコピーの数は? (C++17)
std::string make() { std::string s = "hi"; return s; }
std::string x = make();
2 回
1 回
ムーブ 1 回
0 回(RVO で省略)
C++17 以降はコピー省略が規格で強制されます。関数内の s は呼び出し側の x の場所で直接構築されるので、コピーもムーブも発生しません。