この記事のポイント

  • スマートポインタから裸ポインタ渡し時の責務設計をレビューアーが整理
  • 裸ポインタ露出時に発生する寿命責務問題を読み解く
  • API契約と所有権分離のレビュー観点を体系化

そもそもスマートポインタ→裸ポインタ渡しとは

C++におけるスマートポインタは
所有権と寿命管理責務をコードに埋め込む仕組み
です。一方で

  • 一時的な読み取り用アクセス
  • 外部ライブラリとのインターフェース
  • ポリモーフィズム用の基底クラスAPI

などでスマートポインタのままでは扱えない場面も存在します。
その際に裸ポインタ(T*)へ一時的に変換して渡す場面が発生します。

std::unique_ptr<ApiRequestLog> log = ...;
processLog(log.get());
裸ポインタ渡しの典型用途
  • 読み取り専用API引数
  • 外部ライブラリのC API互換
  • 親子関係による一時的アクセス
  • observer用途の非所有参照

レビューアーは
「裸ポインタに渡した先で寿命責務が崩壊しないか?」
を読み取る役割を担います。

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

スマートポインタ→裸ポインタ渡しは以下の事故源になります。

  • 寿命超過アクセス(所有元終了後の利用)
  • 所有権誤読(受け取る側がdeleteする誤解)
  • API契約不統一(誰が破棄責任か不透明)

レビュー段階で
寿命保持者は常にスマートポインタ側
という設計統一が貫徹されているか確認することが非常に重要です。

レビューアー視点

  • 裸ポインタ渡し先で破棄責任が発生していないか確認
  • 参照先の寿命がスマートポインタ所有内に限定されているか
  • API契約が読み取り専用設計か確認
  • const指定で読み取り専用性を明示しているか
  • スマートポインタを無意味に隠蔽していないか確認

開発者視点

  • スマートポインタ所有を起点に全体寿命統治
  • 裸ポインタは読み取り限定で一時的に利用
  • APIは「読み取るだけ設計」ならconst T*型利用
  • 所有権はスマートポインタ経路でのみ移譲

良い実装例

想定: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());
}

良いポイント

  • スマートポインタが所有権保持
  • 裸ポインタは一時的読み取り専用参照
  • const指定で破壊不可能性保証
  • delete責務は移譲されない

レビュー観点

  • 所有権はスマートポインタ保持側限定か
  • 裸ポインタ先で破棄責務が発生しないか
  • const指定で読み取り専用性が契約化されているか
  • get()利用がスコープ内に留まっているか
  • 参照先寿命が常に有効期間内にあるか
  • API契約内でownership転送になっていないか

良くない実装例: ケース1(裸ポインタで破棄責務移譲)

問題例①
class LogSaver {
public:
    void save(ApiRequestLog* logEntry) {
        logs_.push_back(logEntry);
    }

    void cleanup() {
        for (auto* entry : logs_) delete entry;
    }

private:
    std::vector<ApiRequestLog*> logs_;
};
@Reviewer
delete責務が外部所有物まで含んでいます。所有権移譲契約をunique_ptr受領設計に統一してください。

改善例

改善例①
void save(std::unique_ptr<ApiRequestLog> logEntry) {
    logs_.push_back(std::move(logEntry));
}

良くない実装例: ケース2(裸ポインタ読み取りAPIで破壊操作)

問題例②
void process(ApiRequestLog* logEntry) {
    delete logEntry;
}
@Reviewer
読み取りAPI側でdeleteを行う設計はAPI契約破綻です。裸ポインタは非所有参照専用に限定してください。

改善例

改善例②
void process(const ApiRequestLog* logEntry) { /* 読み取りのみ */ }

良くない実装例: ケース3(所有権意図不明API契約)

問題例③
void handle(ApiRequestLog* logEntry);
@Reviewer
所有権移譲なのか読み取り参照なのか契約が不明確です。const T* もしくは unique_ptr<T> 受領に設計統一してください。

改善例

改善例③
// 読み取りなら
void handle(const ApiRequestLog* logEntry);

// 所有権移譲なら
void handle(std::unique_ptr<ApiRequestLog> logEntry);

観点チェックリスト


まとめ

スマートポインタ→裸ポインタ渡しレビューは
「所有権責務が浮かずに読み取れる設計かを静的に確認するレビュー」です。

レビューアーは常に

  • 所有権責務はスマートポインタ側集中か?
  • get()利用の範囲は適切に狭いか?
  • API契約で寿命違反を誘発しないか?

を読み取り、読み取りAPIの裸ポインタ許容を安全に維持する
レビュー判断を実施します。

UML Diagram