C++ チートシート — クイックリファレンス
C++17 / C++20 の構文、標準ライブラリ、クラス、テンプレート、そして最もよく使われるイディオムを網羅した簡潔なリファレンス。日常の約 80% のシナリオをカバーします。
C++ C++17 / C++20
ISO C++ (GCC / Clang / MSVC) · マルチパラダイム(OOP・ジェネリック・関数型) · 静的・強い型付け・名目型
おすすめの学習パス
まず「Hello World とビルド環境」でコンパイル/実行の流れを掴み、次に変数・型・制御フロー・関数に慣れ、標準ライブラリコンテナ(vector / map)と文字列に進み、RAII とスマートポインタ(メモリセクション)を理解し、その後に OOP とテンプレートを学習します。スレッド、ネットワーク、正規表現、ビルドツールは必要に応じて参照してください。FAQ セクションは落とし穴を避けるための振り返りに向いています。
1.Hello World とビルド環境
C++ プログラムをゼロからコンパイル・実行・構成する方法を学びます。ツールチェーン、コマンドライン引数、複数ファイル、C++20 モジュールを含みます。
最小プログラム
すべての C++ プログラムは main() から実行を開始し、戻り値 0 が成功を表します。std::cout は標準出力ストリームで、<< 演算子で値を送ります。
コンパイルと実行
g++ または clang++ でコンパイルします。-std で C++ 標準バージョンを指定し、-Wall -Wextra で警告を有効化し、-o で出力ファイル名を指定します。実行は ./app で行います。
CMake ビルド
CMake はクロスプラットフォーム対応の主流ビルドシステムです。CMakeLists.txt でプロジェクトとターゲットを宣言し、-B でビルドディレクトリを指定、--build でコンパイルします。生成物は build/ 配下に出力されます。
コマンドライン引数
main の argc は引数の個数、argv は引数文字列の配列です。argv[0] は常にプログラム名となり、ユーザー引数は argv[1] から読み取ります。
終了コード
main の戻り値はプロセスの終了コードとして OS に渡されます。0 が成功、非 0 がエラーの種別を表します。EXIT_SUCCESS / EXIT_FAILURE を使うとより読みやすくなります。
複数ファイルのビルド
宣言はヘッダファイルに置き(#pragma once で多重インクルードを防ぎ)、定義は .cpp に置いて、使用側で #include します。コンパイル時は各 .cpp をまとめてコンパイラに渡しリンクします。
C++20 モジュール
モジュールはヘッダファイルの代替です。export module でモジュールを宣言し、export でシンボルを公開し、import で取り込みます。コンパイルが速くマクロの汚染がない反面、まだ普及段階です。
名前空間
名前空間はシンボルを整理して衝突を防ぎます。geo::area のように修飾してアクセスします。using namespace で全シンボルを一度に取り込むと曖昧になりやすいので、using std::cout のように個別に導入する方が望ましいです。
2.変数と定数
宣言と型推論、const/constexpr、参照、構造化束縛、スコープ、C++ スタイルのキャストについて。
auto 型推論
auto は初期化子から型を推論するため、必ず同時に初期化する必要があります。推論では参照とトップレベルの const は外れるため、必要なら明示的に const auto& と書きます。
const と constexpr
const は値が不変であることを示し、constexpr はコンパイル時に評価されることを保証します。配列サイズやテンプレート引数にも使えます。C++20 以降は constexpr 関数により多くのロジックを含められます。
参照
参照は既存オブジェクトの別名です。宣言時に必ず初期化が必要で、常に同じオブジェクトを指します。引数として参照を渡せばコピーを避けつつ元の値を変更できます。
構造化束縛
C++17 では pair / tuple / 構造体 / 配列を分解して個別の変数に束縛できます。map の反復で key と value を分解するのが最も一般的な使い方です。
インライン変数
C++17 以前、ヘッダ内の非 constexpr グローバル変数は多重定義エラーを引き起こしました。inline 変数を指定すれば、複数の翻訳単位で同じオブジェクトを共有できます。
mutable メンバ
const メンバ関数内でも mutable メンバは変更できます。キャッシュ、呼び出し回数、ミューテックスなど、論理的にオブジェクトの状態を変えない用途で使われます。
decltype
decltype は式の宣言型を取得します。decltype((x)) は括弧が余計につくことで参照として推論される典型的な落とし穴です。後置戻り値型はテンプレート関数でよく使われます。
スコープとシャドウィング
C++ では内側スコープが外側と同じ名前を覆い隠す(シャドウィング)ことができます。グローバル名のシャドウィングは避けましょう。::n と書けば明示的にグローバルにアクセスできますが、可読性は下がります。
リテラルサフィックス
サフィックスでリテラルの型を指定します。LL は long long、f は浮動小数点数、s は std::string です。0b の二進数プレフィックスと ' の桁区切り記号も可読性を高めます。
C++ スタイルキャスト
static_cast はコンパイル時の意味的変換、dynamic_cast は実行時の多態的変換(ポインタ失敗時は nullptr、参照は bad_cast を送出)、const_cast は const を外しますが、元のオブジェクトが非 const である場合に限ります。
3.データ型
組み込み型、列挙型、構造体、テンプレート型、そして std::optional / std::variant / std::any などのモダンなユーティリティ型について。
組み込み型
組み込み型はサイズと符号の有無で区別されます。<cstdint> は int32_t / uint64_t などの固定幅型を提供し、プラットフォーム間で動作が一貫します。
enum class
enum class は素の enum より安全です。値はスコープ修飾してアクセスする必要があり、暗黙的に int に変換されないため、名前漏れや整数の誤用を防げます。
構造体
struct はデフォルトでメンバが公開され、振る舞いのないデータをまとめるのに適しています。デフォルト値を持つメンバがあれば、波括弧による集約初期化で一行でオブジェクトを生成できます。
共用体
union は複数のメンバで同じメモリを共有し、同時に 1 つだけを保持します。標準 C++ では std::variant の方が安全なため、union は基本的に低レベルのメモリ再利用のために使います。
テンプレート型
テンプレートは型をパラメータ化し、使用された型ごとにコンパイラがインスタンスを生成します。ジェネリックプログラミングと標準ライブラリコンテナの基礎です。
型エイリアス
using による型エイリアスは typedef より読みやすく、テンプレートエイリアスも定義できます。エイリアスは新しい型を作るのではなく、同じ型の別名に過ぎません。
optional
std::optional は「値があるかもしれないし、ないかもしれない」戻り値を表現し、番兵値や out 引数の代替となります。bool 文脈で存在を確認し、* で値を取り出します。
variant
std::variant は型安全な共用体で、常に指定された型リストのいずれかを保持します。std::get は指定型を取得し(型不一致なら bad_variant_access)、std::get_if は例外を投げずポインタを返します。
std::any
std::any は任意の型を格納して実行時に取り出せます。異種コンテナやプラグイン引数に適していますが、型消去のコストがあるため、可能なら variant を使う方が望ましいです。
pair と tuple
pair は 2 値、tuple は任意の個数の値を保持します。構造化束縛で要素を名前付き変数に展開すると、std::get のインデックス指定より読みやすくなります。
4.ポインタと配列
生ポインタ、スマートポインタ、配列、参照 —— メモリの所有者とその使い分けについて。
生ポインタ
生ポインタはオブジェクトのアドレスを保持します。* でデリファレンスし、& でアドレスを取得します。生ポインタはメモリを所有しないため、new / delete と組み合わせるかスマートポインタに任せます。
ポインタと配列
式の中では配列名は先頭要素へのポインタに減衰します。p+i はアドレスのオフセット、*(p+i) で値を取得します。これは C から受け継いだ低レベルの挙動です。
nullptr
nullptr は型安全な空ポインタ定数で、NULL や 0 の代替です。null ポインタのデリファレンスは未定義動作なので、必ず使用前に null チェックをしましょう。
new と delete
new はヒープメモリを確保してポインタを返し、delete で解放します。new[] は必ず delete[] と対にします。手動管理はリークしやすいので、RAII コンテナやスマートポインタを優先しましょう。
unique_ptr
unique_ptr は所有権を独占し、スコープを抜ける際に自動で delete します。make_unique は例外安全。所有権の移譲は std::move で行い、コピーは禁止されています。
shared_ptr
shared_ptr は参照カウントで所有権を共有し、カウントが 0 になった時点で解放されます。循環参照によるリークを避けるため、weak_ptr で輪を断ち切りましょう。
weak_ptr
weak_ptr はオブジェクトを所有せずカウントにも影響しません。lock() で一時的に shared_ptr に昇格し(オブジェクトが既に解放されていれば空を返す)、循環参照を断ち切るために使います。
参照 vs ポインタ
参照は構文がシンプルで必ず非 null なので、引数では参照を優先します。「オブジェクトが存在しないかもしれない」「別のオブジェクトを指し直したい」場合にはポインタを使います。
const ポインタ
読み方は右から左へ。int* const はポインタ自体が const、const int* は指し先が const です。混同はよくある混乱ポイントです。
void* ポインタ
void* は「未知の型のメモリ」へのポインタで、間接参照するには明示的に具体的な型へキャストする必要があります。C スタイルの API が任意データを受け渡すために使いますが、C++ ではなるべく避けましょう。
5.制御フロー
if / for / while / switch、そして C++17 で導入された if / switch の初期化ステートメントについて。
if / else
if / else は条件に応じて実行パスを選択します。C++17 以降は if に初期化ステートメントを付与できます(初期化付き if を参照)。
for ループ
古典的な for は初期化・条件・ステップの 3 段構成です。カウントループでは ++i を優先すると、意味的にも一時オブジェクトの生成も減らせます。
範囲 for
範囲 for(C++11)はコンテナの要素を走査し、手作業のインデックスや境界ミスを防ぎます。要素を変更するなら auto&、読み取り専用なら const auto& を使います。
while / do-while
while は条件を先に判定してからループ本体に入り、do-while は最低 1 回は実行されます。回数が不明で実行時の条件に依存するループに適しています。
switch / case
switch は整数 / 列挙値によって分岐し、if の連なりより読みやすくなります。各 case には必ず break(または return)を付けないと、後続の case へフォールスルーします。
break と continue
continue は現在の反復の残りをスキップして次へ進み、break はループ全体を即座に抜けます。多重ループではラベル付き goto で外側まで脱出できます。
初期化付き if
C++17 以降は if / switch で初期化変数を宣言でき、その変数のスコープは分岐内に限定されます。外側へ漏れないため、find-then-check の慣用表現として使えます。
三項演算子
三項演算子は if / else の代入を 1 行で書けます。両分岐の型は互換性が必要で、複雑なロジックでは素直に if の方が読みやすくなります。
goto ラベル
goto はラベル行に直接ジャンプし、構造化された制御フローを壊します。ほぼ常にフラグや return で置き換え可能で、合理的な使い道は多重ループからの脱出だけです。
6.関数とラムダ
宣言と定義、オーバーロード、引数の渡し方、ラムダと関数テンプレート、可変長テンプレート、再帰について。
宣言と定義
宣言はコンパイラにシグネチャを伝え、定義は実装を提供します。宣言はヘッダに何度含まれても問題ありませんが、定義はプログラムにつき 1 つしか許されません。
関数オーバーロード
関数オーバーロードにより、同じ名前の関数を実引数の型でディスパッチできます。コンパイラが最も一致する版を選びます。戻り型だけではオーバーロードを区別できません。
デフォルト引数
デフォルト引数により、呼び出し側で末尾の実引数を省略できます。デフォルト値は右端から連続して指定する必要があり、宣言と定義のどちらか一方でしか指定できません。
引数の渡し方
値渡しはオブジェクト全体をコピーします。const& はコピーを避けて変更も禁止、& は元の値を変更可能、&&(右辺値参照)は一時オブジェクトを受け取りリソースを移譲できます。
戻り値
現代の C++ では RVO / NRVO により一時オブジェクトの戻りがゼロコピーになります。ローカルオブジェクトのアドレスや参照を返してはいけません(ダングリング参照になります)。
ラムダ式
ラムダは匿名の関数オブジェクトです。[] のキャプチャリスト + 引数 + 本体で構成され、<algorithm> と組み合わせてコールバックや比較関数を書く主流の方法です。
ラムダキャプチャ
[=] は全てを値でキャプチャ、[&] は全てを参照でキャプチャ、[x] は指定した変数のみをキャプチャします。参照キャプチャでは元の変数がラムダの生存期間より長く生きる必要があります。
関数テンプレート
関数テンプレートは呼び出し時の実引数から型を推論してインスタンス化されます。1 つの実装で全型に対応できますが、使われる演算子はその型でサポートされている必要があります。
可変長テンプレート
パラメータパック ...Args は任意の個数の実引数を受け取り、畳み込み式 (args + ...) で二項演算子をパック全体に展開できます。printf 形式の自作関数に代わるモダンな手法です。
再帰
再帰は関数が自分自身を呼び出すことで、基底条件で終了します。深い再帰はコールスタックを消費するため、スタックオーバーフローや部分問題の重複計算に注意しましょう。
7.文字列
std::string の基本、連結、検索・置換、フォーマット、文字列ストリーム、数値との相互変換について。
std::string の基本
std::string は可変で伸長可能な文字コンテナで、メモリは自動管理されます。[] の範囲外アクセスは未定義動作なので、例外を投げる at() を使いましょう。
文字列リテラル
"..." リテラルはデフォルトで const char* です。s サフィックスで std::string に、string_view は非所有の読み取り専用ビューで、引数に渡すとコピーを避けられます。
連結と reserve
大量の連結では reserve で事前に容量を確保し、再アロケーションを減らしましょう。+ を繰り返すと一時オブジェクトが生成されるため、ホットパスではバッファの再利用を検討してください。
部分文字列と検索
substr(pos, len) は部分文字列を取り出し、find / rfind は最初にマッチした位置を探します。見つからない場合は std::string::npos を返します。
置換
メンバ関数 replace(pos, len, str) は位置指定で置換し、<algorithm> の std::replace は値指定で全マッチ文字を一括置換します。
フォーマット出力
std::format(C++20)はプレースホルダで整形し、引数の個数をコンパイル時にチェックします。sprintf や cout の連結に代わる手段です。:04d はゼロ詰め、:.2f は小数 2 桁固定です。
stringstream
stringstream はメモリ上の文字列をストリームとして扱います。ostringstream で組み立て、istringstream で >> により解析でき、整形と逆パースに便利なツールです。
文字列と数値の変換
stoi / stod / stoll は文字列を数値にパースし、std::to_string は逆方向に変換します。パース失敗やオーバーフローでは std::invalid_argument / out_of_range が送出されます。
生文字列リテラル
R"(...)" 生文字列リテラル内ではバックスラッシュがそのまま扱われるため、正規表現やパス、複数行テキストでエスケープに悩まず済みます。内容に )" を含む場合は R"tag(...)tag" のようにカスタムデリミタを使います。
文字の走査
範囲 for による文字単位の走査が最も簡潔です。変更したいときは char& を使います。begin / end のイテレータ形式はレガシーコードやアルゴリズムライブラリとの連携に向きます。
8.コンテナとアルゴリズム
vector / array / map / set / 優先度付きキューと、ソート・検索・ranges アルゴリズムについて。
vector
vector は動的配列で、末尾の追加・削除が O(1)、ランダムアクセスも O(1) です。push_back を多用するなら reserve で容量を確保し、先頭挿入は避けましょう(O(n))。
std::array
std::array はスタック上の固定長配列をモダンにラップしたもので、size() / begin() / end() を持ち、アルゴリズムライブラリに渡せます。固定長は array、可変長は vector を使います。
map
std::map は順序付きキー/値ストア(赤黒木)で、キーでソートされ検索は O(log n) です。順序が不要なら unordered_map(平均 O(1))を使います。
unordered_map
unordered_map はハッシュテーブルで、平均 O(1) で検索でき、順序はありません。operator[] は存在しないキーに対してデフォルト値を挿入してしまうため、検索のみなら find または at() を使います。
set
set は順序付きで重複のない集合を保持し、insert / erase / find はいずれも O(log n) です。unordered_set はハッシュ版(順序なし、O(1))。C++20 の contains() で存在判定ができます。
deque と list
deque は両端の追加・削除が O(1)、list は中間挿入が O(1) ですがランダムアクセスは O(n) です。アクセスパターンに合わせてコンテナを選びましょう。
priority_queue(ヒープ)
priority_queue はヒープで、push が O(log n)、top で最大値/最小値が O(1) で取得できます。デフォルトは最大ヒープで、最小ヒープにするには greater を指定します。
ソート
std::sort はランダムアクセスコンテナを O(n log n) でインプレースにソートします。カスタム比較器は「a が b の前に来るか」を返します。list ではメンバ関数 sort() を使います。
検索
順序なしの集合では std::find で線形検索、ソート済みコンテナでは binary_search / lower_bound で二分探索(O(log n))を行います。find はイテレータを返し、end() と比較して命中を判定します。
ranges パイプライン
C++20 の ranges は | で遅延評価されるビューを連結します。filter で抽出、transform で変換でき、コピーもアロケーションも発生せず、シーケンス処理を式として書けます。
9.動的メモリと所有権
RAII、ムーブセマンティクス、スマートポインタの使い分け、例外安全性 —— C++ のリソース管理の中核です。
RAII
RAII はリソースの取得をコンストラクタ、解放をデストラクタに結びつけ、オブジェクト破棄時に自動で解放します。例外安全で明示的なクリーンアップは不要です。
make_unique
make_unique は unique_ptr を生成します。new と構築を 1 ステップで行えるため、構築中に例外が出ても生ポインタはリークしません。配列版は make_unique<T[]>(n) です。
make_shared
make_shared はオブジェクトとコントロールブロックを 1 回のアロケーションで確保します。new の後に shared_ptr で包むより 1 回分少ないです。C++20 以降はコントロールブロックがサイズ調整で無駄を出しません。
ムーブセマンティクス
std::move は左辺値を右辺値参照へキャストし、コピーではなくムーブを発動させます。内部ポインタが移譲され、ソースは空になるため、大規模配列のディープコピーを避けられます。
ムーブコンストラクタ
ムーブコンストラクタはソースのリソースを奪い取りソースを空にするため、コピーより高速です。vector の再アロケーションでコピーではなくムーブを選んでもらえるよう noexcept を付けます。
戻り値最適化
コンパイラは余分なコピー / ムーブ構築を省略し、戻り値を直接呼び出し側に構築します。C++17 以降は値返しされる一時オブジェクトのゼロコピーが保証されています。
循環参照を断ち切る
親 / 子の木構造では、親が shared_ptr を持ち、子が親を指す戻りは weak_ptr にします。さもないと互いに参照し合い、永久に解放されません。lock() で一時的に shared_ptr に昇格し生存を確認します。
noexcept
noexcept は関数が例外を投げないと宣言し、vector の再アロケーションがムーブを選ぶ根拠となります。万一例外を投げると std::terminate で即座に終了するため、確信がある場合のみ付けます。
手動管理のメモリリーク
手動の new / delete は途中で例外が出たり早期 return したりするとリークします。RAII(スマートポインタやコンテナ)ならクリーンアップをデストラクタに任せられ、例外安全です。
10.オブジェクト指向
クラス、アクセス制御、コンストラクタとデストラクタ、継承と多態性、純粋仮想インタフェース、friend、演算子オーバーロードについて。
クラス定義
class はデータと操作をまとめてカプセル化します。メンバはデフォルトで非公開(struct と逆)で、private なデータには public メソッドでアクセスします。
アクセス指定子
public は誰でもアクセス可、protected は派生クラスもアクセス可、private はクラス自身のみ。class はデフォルトで private、struct はデフォルトで public です。
コンストラクタ
コンストラクタはクラスと同名で、メンバ初期化リスト : x(x_) を使うと関数本体での代入より効率的に初期化できます。引数なしのものはデフォルトコンストラクタと呼ばれます。
委譲コンストラクタ
委譲コンストラクタは、同じクラスの別コンストラクタを呼び出して初期化処理の重複を避けられます。委譲先はすでに宣言されている必要があります。
デストラクタ
デストラクタはオブジェクト破棄時に呼ばれ、RAII でリソースを解放する場所です。基底クラスのデストラクタは virtual 必須で、そうでないと基底ポインタ経由で派生オブジェクトを delete した際に派生部分が解体されません。
継承
継承により派生クラスは基底クラスのインタフェースと実装を再利用できます。基底クラスのポインタ/参照経由で virtual 関数を呼ぶと、実行時に多態的にディスパッチされます。
仮想関数と抽象クラス
virtual はオーバーライド可能な関数を宣言し、純粋仮想(=0)にするとそのクラスは抽象クラスとなりインスタンス化できません。virtual を持つクラスではデストラクタも virtual にすべきです。
override と final
override は「基底クラスの virtual を確実にオーバーライドしている」と宣言し、シグネチャが違えば新しい関数を黙って作るのではなくコンパイルエラーにしてくれます。final はそれ以上のオーバーライドを禁止します。
純粋仮想インタフェース
純粋仮想関数 = 0 は実装を持たずインタフェースだけを定義し、それを含むクラスは抽象クラスになります。派生クラスはインスタンス化のために実装しなければなりません。Java のインタフェースに似ています。
friend
friend は指定したクラスや関数に private メンバへのアクセスを許し、カプセル化を破ります。密に結合した場面(演算子オーバーロード、内部イテレータなど)で慎重に使いましょう。
演算子オーバーロード
演算子オーバーロードにより、ユーザー定義型に +、-、<< などの構文を許せます。直感的な意味(+ はオペランドを変更しない、<< は出力など)を保ち、乱用は読みやすさを損ないます。
11.エラー処理
例外の捕捉、標準例外階層、独自例外、noexcept、そして optional / expected による例外なしのエラー表現について。
try / catch
try ブロック内で投げられた例外は catch で捕捉されます。std::exception& の基底クラスで受け、必要なら具体的な型ごとに分岐します。捕捉されなかった例外は呼び出しスタックを遡ります。
参照による捕捉
const& で捕捉すれば多態性を保ったままゼロコピーで受け取れます。値で捕捉するとスライス(派生部分の情報が落ちる)が起こります。catch(...) は何でも捕捉しますが型情報は失われます。
標準例外階層
標準例外は std::exception から派生します。具体的な型の catch を先に、std::exception& のフォールバックを最後に置き、最も具体的なマッチが選ばれます。
独自例外
独自例外は std::runtime_error(やその兄弟)から派生させ、using でコンストラクタを継承します。what() がメッセージを保持し、呼び出し側では基底クラスで捕捉すれば十分です。
noexcept と例外
noexcept は例外を投げないと約束し、vector の再アロケーションやムーブはこれに依存します。万一例外を投げると std::terminate で即座に終了するため、確信が持てない場合は付けないでください。
optional で失敗を表す
「失敗が想定される」操作では std::optional を返す方が例外より軽量で、呼び出し側は空の分岐を明示的に扱えます。例外は本当に予期しないエラーに取っておきましょう。
std::expected
std::expected(C++23)は値とエラー記述のどちらかを保持します。optional より情報量が多く、例外より挙動が予測可能で、パースやバリデーションのコードに適しています。
例外安全性
例外安全性は RAII で担保します。クリーンアップをローカルオブジェクトのデストラクタに入れることで、正常 return でも例外送出でも必ず実行され、リークや状態の不整合を防ぎます。
errno と C の連携
errno は C 時代のグローバルなエラーコードで、次の失敗呼び出しで上書きされ、スレッドセーフではありません。C++ では例外を優先し、errno は C 関数と連携するとき直ちに読むだけにしましょう。
12.ファイルとストリーム I/O
ファイルの読み書き、行ごとの読み込み、フォーマット出力、filesystem のパス、 binary ファイルについて。
ファイル書き込み
ofstream でファイルを書き込み用に開き、<< でフォーマット出力します。デストラクタで自動的に閉じますが、明示的に close() を呼べば書き込み失敗を早く検出できます。デフォルトでは既存ファイルを上書きします。
ファイル読み込み
ifstream でファイルを読み込み用に開き、getline で 1 行ずつ読み込みます。ループ条件は getline の戻り値で、EOF か失敗に当たると終了します。
行読みと単語読み
getline は 1 行全体を読み、>> は空白をスキップして 1 単語を読みます。混在させる場合、>> の後に残る改行文字を ignore() で捨てないと、次の getline が空文字列を返します。
フォーマット制御
<iomanip> の setw / setprecision / fixed で出力フォーマットを制御します。表形式で桁を揃えたり小数点以下の桁数を抑えたりするときに便利です。
メモリ上のストリームをファイルへ
ostringstream でメモリ上にまとめてから一度だけファイルに書き出すと、ディスク I/O を減らせます。入力のパースでは istringstream で先に検証すると安全です。
filesystem ディレクトリ
<filesystem>(C++17)はパスとディレクトリを扱います。exists / create_directories / 反復処理など、文字列でパスを組み立てるより堅牢で、区切り文字はプラットフォームに合わせて自動処理されます。
パスの分解
fs::path はパスの分解と結合を扱い、プラットフォームごとに正しい区切り文字を使います。filename / extension / parent_path で構成要素を取り出せます。
バイナリファイル
バイナリモード std::ios::binary では改行の変換は行われず、write / read はバイト列単位で読み書きします。構造体を書き出すときはメモリのアラインメントとエンディアンに注意しましょう。
標準入力の検証
cin >> が失敗するとストリームが fail 状態になり、その後の読み込みは全て失敗します。clear() で状態をリセットし、ignore() で不正入力を破棄してから続行します。読み込み後は必ず cin の状態を確認しましょう。
13.よくある落とし穴
C++ の代表的な 10 個の落とし穴 —— 正しい書き方は GOOD、間違った書き方は BAD で色分けしています。
符号付きと符号なしの混在
符号付きと符号なしを混在させて比較すると、符号付きが暗黙的に符号なしへ変換され、-1 は巨大な正の数になります。比較前に明示的にキャストするか符号の有無を揃えましょう。
ダングリング参照
ローカル変数の参照やポインタを返すとダングリング参照になります。関数から戻った時点でメモリは無効になり、使用すると未定義動作です。値で返すかオブジェクトの寿命を延ばしましょう。
基底クラスのデストラクタが非 virtual
基底クラスのポインタ経由で派生オブジェクトを delete する場合、基底クラスのデストラクタは virtual でなければなりません。そうでないと派生部分が解体されず、リソースがリークします。
整数除算
整数同士の除算は整数になり(切り捨て)、double に代入しても少数部は戻りません。少なくとも片方を浮動小数点にキャストしてから割りましょう。
ループ内の文字列連結
s = s + x は毎回既存の文字列全体をコピーして一時オブジェクトを作るため O(n²) になります。reserve + += でその場で追加すれば O(n)。大規模連結では桁違いの差になります。
using namespace std
ヘッダ内で using namespace std を書くと、インクルードする全ファイルに std のシンボルが漏れて名前衝突の原因になります。using 宣言を個別にするか std:: で修飾しましょう。
i++ か ++i か
i++ は古い値を返すためコピー / 一時オブジェクトが必要で、++i は直接インクリメントします。int では差が出ませんが、ユーザー定義型やイテレータでは ++i の方がコピーを 1 回減らせます。
const の正しさ
読み取り専用の引数は const& を使うと、意味が自己説明的になりコピーも避けられます。const を正しく付けるとコンパイラが意図しない変更を検出し、呼び出し側にも引数を変更しないことが伝わります。
マクロの濫用
マクロは型チェックを素通りし、スコープも無視し、引数を複数回評価する副作用が出ます。const / constexpr 関数やテンプレートで済むならそちらを優先しましょう。
ループでの値コピー
範囲 for で値を使うと各要素をコピーします。大きなオブジェクトは const auto& で読み取り、auto& で変更します。サイズが小さくコピーが欲しいときだけ値にしましょう。
14.スレッドと並行処理
std::thread、ミューテックス、アトミック操作、条件変数、非同期タスクについて。
スレッドの作成
std::thread は新しいスレッドを生成して呼び出し可能オブジェクト(関数 / ラムダ)を実行します。スレッドオブジェクトは破棄前に必ず join か detach する必要があり、そうでないと std::terminate が呼ばれます。
join と detach
join は現在のスレッドをブロックして子スレッドの終了を待ち、detach は子スレッドを独立に走らせます。detach 後は join できず、アクセスするオブジェクトは生存し続ける必要があります。
mutex
mutex は共有データを保護し、同時に 1 つのスレッドだけが保持できるようにします。手動の lock / unlock は漏れやすいので、lock_guard や unique_lock を優先しましょう。
lock_guard
lock_guard は RAII でミューテックスを管理します。構築時にロックし、破棄時にアンロックするため、例外を含むどの抜け道でも必ずアンロックされます。標準的なロック取得手段です。
アトミック操作
std::atomic は基本型にロックフリーのアトミック操作を提供します。fetch_add / load / store はすべてアトミックで、ミューテックスよりオーバーヘッドが小さいです。
condition_variable
condition_variable は条件待ちに使います。wait でロックを解放しつつブロックし、notify で起こします。誤った起床に備えて wait には必ず述語を渡ししょう。
async / future
std::async は非同期タスクを起動して future を返し、get() で結果を待ちます。手書きの thread + 共有変数よりシンプルで、std::launch::async を指定すれば新しいスレッドが必須になります。
thread_local
thread_local 変数はスレッドごとに独立したコピーを持ち、ロック不要のキャッシュ・カウンタ・スクラッチ状態として自然に働きます。スレッド終了時に破棄され、スレッドプライベートデータに適します。
データ競合
2 つのスレッドが同じ非アトミック変数を同時に読み書きするとデータ競合になり、結果は未定義で予測不能です。atomic か mutex で同期しましょう。
並列アルゴリズム
C++17 の <execution> はアルゴリズムに実行ポリシーを追加します。par は並列、unseq はベクトル化。大量のデータを自動的にマルチコア活用しますが、要素が共有可变状態を持たないことが前提です。
15.ネットワーク(ソケット)
POSIX ソケットの作成、listen、connect、send / recv、タイムアウト制御について(クロスプラットフォームなネットワークには Boost.Asio や libcurl を推奨)。
ソケットの作成
POSIX ソケットはネットワーク I/O の基盤です。socket() で作成し、AF_INET は IPv4、SOCK_STREAM は TCP を意味します。返り値はファイルディスクリプタで、-1 は失敗です。
bind と listen
bind でソケットをポートに紐付け、listen で接続待ちに入ります。htons はホストバイト順からネットワークバイト順へ変換します。sockaddr_in は IPv4 アドレスを表す構造体です。
accept
accept はキューからクライアント接続を取り出し、新しいソケットを返します。接続ごとに fd が 1 つ割り当てられ、元の待ち受け fd は次の accept に使われ続けます。
クライアント connect
クライアントは socket() + connect() でサーバアドレスへ接続します。inet_pton はドット表記の IP をバイナリへ変換します。connect 失敗は -1 を返します。
send と recv
send / recv は TCP ソケット上でバイトを送受信します。recv が 0 を返すのは相手が切断、-1 はエラーです。TCP はバイトストリームのため、メッセージ境界は自分で定義します。
ホスト名解決
getaddrinfo はホスト名とサービスを connect 可能なアドレスのリストに変換し、IPv4 / IPv6 を自動で扱います。手書きの inet_pton + ポート指定に代わる推奨手段です。
タイムアウト制御
SO_RCVTIMEO で recv にタイムアウトを設定でき、満了時は -1 が返り errno=EWOULDBLOCK になります。ブロックする recv を制御可能にし、永遠のハングアップを防げます。
最小限の HTTP リクエスト
HTTP リクエストはテキストプロトコルで、リクエスト行 + ヘッダ + 空行で構成されます。デモではソケットへ生で送信していますが、本番では libcurl(リダイレクト、TLS、圧縮を自動処理)を使いましょう。
16.時間と日付
chrono の duration / time_point、2 種類のクロック、フォーマット、スリープ、経過時間の計測について。
duration
duration は時間の長さを型付きの単位で表します。seconds / milliseconds / microseconds などがあり、リテラルサフィックス s / ms / us で直感的に書けます。duration_cast で単位をまたいで変換します。
time_point
time_point は時間軸上の特定の瞬間で、クロックと duration のオフセットで構成されます。time_point に duration を加減算すれば別の瞬間へ移動でき、2 つの time_point を引くと duration が得られます。
system_clock
system_clock はシステムの壁時計に対応し、to_time_t / from_time_t で time_t と相互変換できます。カレンダー時刻の表示やログのタイムスタンプに使いますが、システム時刻変更の影響を受けます。
steady_clock
steady_clock は単調増加でシステム時刻変更の影響を受けないため、経過時間の計測やデッドラインの判定など、計時の基準として使います。カレンダー表示には system_clock を使いましょう。
時間のフォーマット
put_time は strftime 形式で time_t をローカル時刻文字列に整形します。%Y-%m-%d %H:%M:%S が最もよく使われます。localtime は静的バッファへのポインタを返すためスレッドセーフではありません。
スレッドのスリープ
sleep_for は指定した長さだけ、sleep_until は指定した時刻まで現在のスレッドをスリープさせます。メインスレッドを止めると UI が固まるため、バックグラウンドやテスト用途に限定します。
経過時間の計測
計測のイディオムは、開始時刻を記録 → 終了時刻との差で duration を取得 → duration_cast で単位変換 → count() で値を取り出す、という流れです。steady_clock を使えばシステム時刻変更の影響を受けません。
C スタイルの時間変換
time_t は秒単位のタイムスタンプで、gmtime で UTC 構造体、localtime でローカル構造体に変換し、strftime で整形します。これらは静的バッファを使うためスレッドセーフではありません。
タイムゾーン
C++20 の zoned_time はタイムゾーン付きで時刻を出力し、夏時間(サマータイム)を自動で扱います。古い標準ライブラリにはタイムゾーンサポートがないため、システムの localtime やサードパーティライブラリで補いましょう。
17.プロセスとシグナル
system、fork / exec / wait による子プロセス、環境変数、シグナル処理、パイプ経由の出力取得(POSIX 概念)について。
system
system() はシェルに文字列コマンドを渡して実行します。手軽ですが、コマンドインジェクションやシェルの差異により安全ではありません。出力を取りたい / 引数を渡したい場合は fork + exec や popen を使います。
fork
fork は現在のプロセスを複製して子プロセスを作ります。親プロセスには子の pid が、子プロセスには 0 が返り、-1 は失敗です。fork 後、両プロセスは fork の呼び出し位置から実行を続けます。
exec
exec ファミリは現在のプロセス内に新しいプログラムを読み込んで自身を置き換えます。fork + exec を組み合わせて外部コマンドを起動します。成功時は exec から戻らず、失敗時は -1 を返すため、子プロセス側で失敗パスを処理します。
wait
waitpid は指定した子プロセスの終了を待ち、ステータスを取得します。WIFEXITED は正常終了を判定し、WEXITSTATUS は終了コードを取り出します。wait しない子プロセスはゾンビになります。
環境変数
getenv は環境変数を読み(無ければ nullptr)、setenv は設定、unsetenv は削除します。環境変数は親プロセスから子プロセスへ渡されるシンプルな設定チャネルです。
signal
signal でシグナルハンドラを登録し、SIGINT(Ctrl+C)や SIGTERM(kill のデフォルト)を受け取れます。ハンドラ内では非同期シグナル安全な操作だけを行い、メモリアロケーションや I/O は避けましょう。
sigaction
sigaction は signal より信頼性が高く、シグナルマスクやフラグを設定できます。SA_RESTART を付ければシグナルで中断されたブロッキング呼び出しが自動で再開します。本番コードでは sigaction を優先しましょう。
exit と _exit
exit はプロセスを終了し、atexit ハンドラを実行し、バッファをフラッシュします。_exit は何もせず即座に終了します。fork 後の子プロセスでは親リソースの二重解放を防ぐため _exit を使うべきです。
popen
popen はコマンドを実行し、その出力をパイプで読みます("r" で読み / "w" で書き)。system と異なり出力を取り込めるのが利点です。pclose でクローズしつつ終了ステータスを取得します。
18.正規表現
std::regex のマッチ、検索、置換、キャプチャグループ、フラグ、生文字列リテラルの書き方について。
基本的なマッチ
std::regex はデフォルトで ECMAScript 文法を使います。regex_match は文字列全体の一致を要求し、regex_search は部分文字列を探します。R"()" 生文字列リテラルを使えばエスケープ不要です。
全体マッチとグループ
regex_search は最初にマッチした部分文字列を見つけ、smatch に全体(m[0])とキャプチャグループ(m[1]…)を保存します。regex_match は文字列全体の一致を要求し、フォーマット検証に向きます。
全マッチの走査
regex_search を前のマッチの末尾から繰り返すことで全マッチを走査します。m.suffix().first はマッチ以降の残りの開始点、または regex_iterator を直接使っても良いでしょう。
置換
regex_replace は全マッチをまとめて置換します。置換テンプレートでは $& がマッチ全体、$1 がキャプチャグループを参照します。位置を指定して置換したい場合は regex_iterator を使いましょう。
sregex_iterator
sregex_iterator は「全マッチを列挙する」をイテレータにカプセル化したもので、手書きの while regex_search ループより簡潔で、大量抽出に適しています。
キャプチャグループ
キャプチャグループは丸括弧で定義し、smatch をインデックスで参照します。(?:...) は番号を消費しない非キャプチャグループ、名前付きグループ (?<name>...) は m["name"] でアクセスします。
マッチフラグ
std::regex のコンストラクタの第 2 引数はフラグです。icase は大小文字無視、multiline は ^ / $ を行頭・行末に、ECMAScript / extended は文法方言を選びます。複数指定は | で組み合わせます。
生文字列で正規表現を書く
R"(...)" 内ではバックスラッシュがそのまま扱われるため、正規表現やパスをエスケープなしで書けます。)" を含む場合は R"tag(...)tag" のようにカスタムデリミタを指定します。正規表現は常に生文字列リテラルで書きましょう。
正規表現の例外
不正な正規表現は構築時に std::regex_error を送出します。クラッシュを防ぐため捕捉しましょう。コンパイルコストの高い正規表現はオブジェクトを再利用して再コンパイルを避けます。
19.ビルドとデバッグ
コンパイラフラグ、Makefile / CMake、フォーマッタ、サニタイザ、デバッガ、プロファイラについて。
主なコンパイルフラグ
よく使うフラグ:-std で標準を選択、-O で最適化レベル、-g でデバッグ情報、-Wall -Wextra で警告、-Werror で警告をエラー化。複数ファイルは -c で個別コンパイルしてからリンクします。
Makefile
Makefile はターゲットと依存を宣言し、make が古いものだけ再ビルドします。$< は最初の依存、$@ はターゲット、$^ は全依存。レシピ行は必ずタブでインデントします。
CMake
CMake は宣言的なビルドシステムで、Makefile や VS プロジェクトをクロスプラットフォームに生成します。target_link_libraries でライブラリをリンクし、find_package で依存を見つけます。ビルドは独立した build/ ディレクトリで行います。
pkg-config
pkg-config はライブラリのコンパイル / リンク用フラグを照会し、$(pkg-config ...) でシェル置換によりコマンドラインへ注入します。手書きのパスを避けられ、サードパーティライブラリをリンクする標準的な手段です。
clang-format
clang-format はコードを自動整形し、.clang-format ファイルでスタイルを統一します。ColumnLimit で行幅、SortIncludes でインクルード順を制御できます。コミット前に実行すると一貫性が保てます。
サニタイザ
サニタイザはコンパイル時に有効化すると、実行時にメモリ / 並行性のバグを検出します。address は境界外 / リーク、undefined は未定義動作、thread はデータ競合。テストでは必ず有効にしましょう。
gdb
gdb はコマンドラインのデバッガで、-g を付けてコンパイルするとシンボル情報が埋め込まれます。break でブレークポイント、run で起動、bt でバックトレース、print で値を確認。VSCode / CLion の GUI デバッガも内部では gdb を使っています。
valgrind
valgrind は実行時のメモリ問題(未初期化読み、境界外、リーク、二重解放)を検出します。--leak-check=full を付ければ各リークの呼び出しスタックが表示されます。低速なのでテストの一部に対して実行すれば十分です。
静的ライブラリ
ar は複数の .o を静的ライブラリ lib*.a にまとめます。-L でライブラリディレクトリを、-l でライブラリ名(lib プレフィックスと .a サフィックスは省略)を指定します。静的ライブラリは実行ファイルにリンクされるため、実行時の依存はありません。
プロファイリング
gprof / perf でホットな関数をプロファイリングします。g++ -pg で計装して実行し、gprof で関数ごとの呼び出し回数と時間配分を出力して最適化ポイントを探します。
このチートシートについて
本ページは C++17/C++20 の自己完結型チートシートであり、言語コア、標準テンプレートライブラリ(STL)、ビルドツールチェーンについて、実プロジェクトで多用される約 80% の一般的な用法をカバーしている。内容はモダンな慣用句を重視し、スマートポインタ(unique_ptr / shared_ptr)、ムーブセマンティクス、auto による型推論、範囲 for、構造化束縛、そして C++20 の concepts と ranges を扱う。C++ は Bjarne Stroustrup が 1985 年に開発した言語で、C の高いパフォーマンスと高水準の抽象化能力を兼ね備え、システムプログラミング、ゲームエンジン、高性能計算の基盤となっている。 19 の章はそれぞれ一つのテーマに特化しており、基礎文法、変数、型とポインタ、制御フロー、関数、文字列、コレクション、メモリ管理、オブジェクト指向、エラー処理、入出力、よくある落とし穴、並行処理、ネットワーク、時間、プロセス、正規表現、ビルドツールを扱っている。各セクションには「概念の紹介 + そのままコピーできるコードスニペット」が付属し、查阅や実験に使いやすい。 すべてのコードと文章はブラウザ上でローカルにレンダリングされ、データは一切端末から外部へ送信されない。信頼すべきリファレンスは cppreference および ISO C++ 標準草案を参照されたい。
バージョン 2.1.0