この記事のポイント

  • ポインタの有効範囲(スコープ)制限設計をレビューアーが読み解く技術を整理
  • 寿命責務集中・安全参照設計をレビュー観点として整理
  • スコープ外露出防止による安全設計判断力を養う

そもそもポインタ有効範囲(スコープ)制限とは

C++におけるポインタは
「スコープ外でも生き残れる危険性を持つ参照手段」
です。スコープから外れた瞬間に寿命が切れるオブジェクトを参照し続けると、未定義動作となります。

ApiRequestLog* getLog() {
    ApiRequestLog log;  // スコープ終了で破棄
    return &log;        // ダングリングポインタ
}
スコープ制限が重要になる設計理由
  • ダングリングポインタ発生防止
  • 所有権浮遊の予防
  • 寿命保証経路の明確化
  • 読み取り/書き込みの責務整理

レビューアーは
「この参照は生存保証されているか?」
を静的に読み取ることが常に求められます。

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

ポインタ設計において有効範囲管理を誤ると以下が発生します。

  • use-after-freeバグ
  • スレッド間寿命崩壊
  • 破棄漏れによるリソースリーク
  • 不定期クラッシュ・断続性バグ

レビュー段階で
参照寿命が読めるスコープ構造に整理されているか
を確認することが重要です。

レビューアー視点

  • スマートポインタ利用で寿命管理が集中されているか
  • get()経由の裸ポインタ取得範囲が狭く閉じているか
  • スコープ超過後に参照が残らない設計か
  • 非同期処理連携時に寿命保証経路が存在しているか
  • 静的解析時に寿命流れが読み取れる構造か

開発者視点

  • まずスマートポインタに寿命責務を集約
  • get()利用は一時的読み取り参照に限定
  • 裸ポインタ保持を構造禁止(即時利用のみ許容)
  • 所有権移譲はmove表現で明示
  • ラムダ/非同期処理連携時に寿命保証設計を入れる

良い実装例

想定:ApiRequestLogのスコープ集中型所有権設計

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

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

class LogPrinter {
public:
    void print(const ApiRequestLog* logEntry) {
        if (!logEntry) return;
        std::cout << logEntry->requestId << std::endl;
    }
};

void process() {
    auto log = std::make_unique<ApiRequestLog>();
    log->requestId = 123;

    LogPrinter printer;
    printer.print(log.get());
}

良いポイント

  • スマートポインタ所有に寿命責任集中
  • get()による裸ポインタ利用は即時消費型
  • スコープ超過後にポインタ露出なし
  • 例外安全保証も自然吸収

レビュー観点

  • 裸ポインタ利用範囲が局所的かつ即時消費型か
  • スマートポインタ側で寿命保証が集中されているか
  • API契約が所有権転送を含んでいないか
  • スコープ超過参照が構造上起こらないか
  • スレッド間受け渡し時も寿命保証構造があるか

良くない実装例: ケース1(スコープ超過型ダングリング)

問題例①
ApiRequestLog* create() {
    ApiRequestLog log;
    return &log;
}
@Reviewer
スコープ超過でダングリングポインタが発生します。所有オブジェクトはスマートポインタで保持し、返却側に移譲してください。

改善例

改善例①
std::unique_ptr<ApiRequestLog> create() {
    return std::make_unique<ApiRequestLog>();
}

良くない実装例: ケース2(裸ポインタ保持型設計)

問題例②
class LogHolder {
public:
    void set(ApiRequestLog* logEntry) {
        log_ = logEntry;
    }
private:
    ApiRequestLog* log_;
};
@Reviewer
裸ポインタ保持は寿命責務が外部依存化します。unique_ptr管理に統一してください。

改善例

改善例②
void set(std::unique_ptr<ApiRequestLog> logEntry) {
    log_ = std::move(logEntry);
}

良くない実装例: ケース3(非同期寿命保証崩壊)

問題例③
void asyncProcess(ApiRequestLog* logEntry) {
    std::thread([=]{
        std::cout << logEntry->requestId << std::endl;
    }).detach();
}
@Reviewer
裸ポインタを非同期クロージャに渡すと寿命保証崩壊します。shared_ptrで寿命共有保証を組み込んでください。

改善例

改善例③
void asyncProcess(std::shared_ptr<ApiRequestLog> logEntry) {
    std::thread([logEntry]{
        std::cout << logEntry->requestId << std::endl;
    }).detach();
}

観点チェックリスト


まとめ

スコープ制限レビューは
「寿命流れを静的に全経路追える構造になっているかを確認するレビュー」です。

レビューアーは常に

  • スマートポインタ中心に寿命集約されているか?
  • get()露出は即時使用で閉じているか?
  • 非同期・コールバック系でも寿命保証経路が明示されているか?

を静的に読み解き、寿命流出防止設計へ誘導する役割を担います。

UML Diagram