この記事のポイント

  • ヒープとスタックの使い分け設計をレビューアーが読み解く技術を整理
  • 寿命責務・所有権・パフォーマンスの設計判断をレビュー視点で解説
  • new/delete露出の是非を読み取るレビュー力を養う

そもそもヒープとスタックとは

C++におけるメモリ配置は大きく次の2種類に分類できます。

領域 特徴
スタック 自動変数、関数スコープ依存、自動解放、高速、容量制限
ヒープ new/delete管理、寿命自由、手動解放必要、断片化注意
比較ポイント整理
  • 寿命:スタックはスコープ従属、ヒープは自由
  • 速度:スタックは非常に高速(ポインタ加算のみ)
  • 安全性:スタックは自然解放、ヒープは解放漏れリスク
  • 容量:ヒープは大容量可能、スタックは上限制約あり

レビューアーは「なぜこの配置選択が採用されたのか?」を静的に読み取ります。

なぜこれをレビューするのか

ヒープ/スタックの使い分けは設計全体の保守性・安全性・パフォーマンスに直結します。

  • ヒープ不要な場面でのnew多用 → 解放責務分散
  • スタックで巨大オブジェクト確保 → スタックオーバーフロー事故
  • 寿命誤読によるuse-after-free発生

レビュー段階で
寿命と所有権を静的に読み解く習慣が最重要です。

レビューアー視点

  • 寿命管理をスコープ管理で吸収できるか確認
  • ヒープ配置は解放責任が集中しているか確認
  • new露出が不要でないか静的に評価
  • サイズ・容量をスタック制約に合わせて整理できるか確認
  • 例外安全性を構造保証できるか評価

開発者視点

  • まずスタック前提で設計を考える
  • サイズ超過や寿命要件で必要時のみヒープ選択
  • ヒープ採用時はRAIIを前提に責務集中
  • コンテナ活用で動的寿命管理を構造吸収

良い実装例

想定:APIリクエストログのスタック集中モデル

良い設計例
#include <string>
#include <iostream>

struct ApiRequestLog {
    int requestId;
    std::string endpoint;
    std::string clientIp;
    int responseCode;
    time_t requestedAt;
};

void process() {
    ApiRequestLog log {
        1001, "/api/items", "192.168.0.10", 200, std::time(nullptr)
    };

    std::cout << log.requestId << std::endl;
}

良いポイント

  • スタック上の自動変数で寿命管理
  • スコープ終了で自動解放
  • new/delete完全排除
  • 例外安全自然保証

レビュー観点

  • newが必要な場面か?(不要new排除)
  • スタックサイズ内に収まる設計か
  • RAIIによる自動解放構造に寄せられているか
  • ヒープ採用時は所有権集中・RAII統合できているか
  • API契約に解放責務を転嫁していないか
  • 例外発生時にも安全か

良くない実装例: ケース1(不要new使用)

問題例①
void process() {
    ApiRequestLog* log = new ApiRequestLog();
    log->requestId = 1001;
    std::cout << log->requestId << std::endl;
    delete log;
}
@Reviewer
new/deleteを使用する必要がありません。スタック自動変数で寿命管理してください。RAII構造に統一しましょう。

改善例

改善例①
ApiRequestLog log {1001, "/api/items", "192.168.0.10", 200, std::time(nullptr)};

良くない実装例: ケース2(巨大スタック構造)

問題例②
void process() {
    char buffer[10 * 1024 * 1024];  // 10MBスタック確保
}
@Reviewer
スタック容量超過リスクがあります。巨大バッファはヒープ(std::vector等)管理へ移行してください。

改善例

改善例②
std::vector<char> buffer(10 * 1024 * 1024);

良くない実装例: ケース3(寿命管理の分散)

問題例③
class LogSession {
public:
    void start() {
        log_ = new ApiRequestLog();
    }

    void end() {
        delete log_;
    }

private:
    ApiRequestLog* log_;
};
@Reviewer
new/deleteはRAII構造で集中管理してください。unique_ptr管理に移行しましょう。end()自体が不要になります。

改善例

改善例③
class LogSession {
public:
    LogSession() : log_(std::make_unique<ApiRequestLog>()) {}

private:
    std::unique_ptr<ApiRequestLog> log_;
};

観点チェックリスト


まとめ

ヒープ/スタックレビューは
「寿命責任がスコープに埋め込めるかを読み取るレビュー」です。

レビューアーは常に

  • newを使わずに済ませられないか?
  • 巨大オブジェクトは安全に管理できているか?
  • 解放責務は自然解放構造に吸収されているか?

を静的に読み取り、寿命集中+安全領域設計へ誘導するのが役割です。

UML Diagram