C++ Learning

第23回 確認問題(コンテナをもう少し)

STEP 3 で扱った array / string_view / map / set / deque の総合確認クイズです。全 12 問、カテゴリ別に採点。

進め方

スコア 0 / 12 回答済み 0 array string_view map set/deque 選び分け
array std::array 0 / 2

Q1 std::array<int, 5> にできない操作は?

a[2] へのアクセス
a.push_back(6)
a.size()
for (auto x : a)
array はサイズ固定。要素数を変える push_back / pop_back / erase / resize / clear は存在しません。サイズが変わるなら vector を使います。

Q2 C の int a[5] に対する std::array<int, 5> の利点として正しくないのは?

.size() メソッドでサイズが取れる
= で配列ごとコピーできる
関数に渡してもサイズが保たれる
ヒープに確保される
array は vector と違いスタック確保(C 配列と同じ)。ヒープを介さないので高速で、constexpr 文脈でも使えます。
string_view std::string_view 0 / 2

Q3 std::string_view の内部が持つデータは?

文字列の全コピー
SSO バッファと長さ
ポインタと長さだけ
ハッシュ値とポインタ
string_view は const char*(ポインタ)と size_t(長さ)の 2 つだけ。自分では文字列データを所有せず、他人のメモリを覗くだけ。だから高速ですが、寿命に注意。

Q4 次のコードで問題があるのはどれ?

std::string s = "hi"; std::string_view sv = s; // 使う
std::string_view sv = make_string(); // 戻り値は一時オブジェクト
void f(std::string_view s); f("hello");
std::string_view sv = "literal"; std::cout << sv;
②は一時オブジェクトの文字列データを sv で指そうとするが、一時オブジェクトはその式の終了で破棄されるためダングリングに。①と④は元の文字列が sv より長く生きるので OK、③は関数内で使うだけなのでリテラルの寿命で足りる。
map std::map / unordered_map 0 / 3

Q5 std::map<std::string, int> m;m["x"] = 10; した後、m["y"] を読むとどうなる?

例外 std::out_of_range
コンパイルエラー
0 が返り、かつ {"y": 0} が挿入される
空文字列が返る
[] は「存在しなければ勝手に挿入」する挙動。int なら 0、string なら "" でデフォルト構築されます。存在確認には count / find / contains(C++20)を使いましょう。

Q6 std::map の検索の計算量は?

O(1)
O(n)
O(log n)
O(n log n)
map は平衡二分木で O(log n)。O(1) 平均が欲しければ unordered_map(ハッシュテーブル)。

Q7 map を全要素ループする最もモダンな書き方は?

for (int i = 0; i < m.size(); ++i) ...
for (auto it = m.begin(); it != m.end(); ++it) ... (*it).first
for (const auto& p : m) std::cout << p.first << p.second
for (const auto& [k, v] : m) std::cout << k << v
C++17 の構造化束縛と範囲 for の組み合わせが最も読みやすい。意味のある名前で受けられます。①は map にインデックスアクセスできないので動きません。
set/deque std::set / std::deque / std::list 0 / 2

Q8 std::set<int> s; s.insert(3); s.insert(3); s.insert(5); の後、s.size() は?

3
2
1
0
set は重複を許さない。3 を 2 回入れても 1 つだけ。3 と 5 で size は 2。複数保持したければ multiset か vector を使います。

Q9 std::dequestd::vector に勝るケースは?

キャッシュ効率が重要な数値計算
先頭への要素追加が頻繁
ランダムアクセスの速さ
constexpr 文脈で使いたい
deque は両端に O(1) で追加できるのが唯一の独自強み。vector の push_front は O(n) で遅いため、先頭追加が多い場面では deque が適任。逆に要素のランダムアクセスやキャッシュ効率は vector のほうが速い。
選び分け コンテナ選定 0 / 3

Q10 「ユーザーが入力した単語を重複排除して、入力順は保ちたい」に適した組み合わせは?

std::set<std::string> だけ
std::unordered_set だけ
std::vector<std::string> + std::unordered_set で存在チェック
std::map<int, std::string>
std::list<std::string>
set / unordered_set は入力順を保存しない(ソート順 or 順序不定)。入力順を保ちつつ重複排除なら、vector に追加順で入れつつ unordered_set で既出チェック、が定番パターン。

Q11 「商品の在庫:商品コード→在庫数、商品コード順に定期的に出力」に適したのは?

std::unordered_map<std::string, int>
std::map<std::string, int>
std::vector<std::pair<std::string, int>>
std::set<std::string>
「キー → 値」かつキー順に出力したいので std::map が最適。unordered_map は順序不定。vector<pair> はソートや検索を自分で書く必要あり。

Q12 C++ で「迷ったらまず選ぶ」推奨コンテナは?

std::list
std::vector
std::deque
std::set
モダン C++ の一般則:迷ったら vector。メモリ連続でキャッシュ効率が良く、ほとんどのケースで十分高速。他のコンテナは vector で不足が判明してから切り替える。

お疲れさまでした 🎉

0 / 12
次へ: STEP 4(クラス基本)へ