C++レビュー|new/delete箇所の集中管理と安全設計レビュー観点整理
この記事のポイント
- new/delete使用箇所の集中管理設計をレビューアーが読み解く技術を整理
- 解放責務の分散排除と所有権明示のレビュー観点を整理
- スマートポインタ移行とRAII設計徹底のレビュー技術を解説
そもそもnew/delete集中管理とは
C++におけるnew/deleteは動的メモリ管理の最低レイヤです。
極めて柔軟ですが、以下のリスクを常に孕んでいます。
- 解放漏れ(メモリリーク)
- 二重delete(未定義動作)
- 例外発生時の途中解放不能
設計の基本は
「new/deleteの露出を可能な限り狭いスコープに集中させる」
ことにあります。
集中管理の基本原則
- new/deleteはコンストラクタ+デストラクタ内限定が基本形
- 呼び出し側へ解放責任を転嫁しない
- RAII徹底による自動解放に寄せる
- 可能な限りスマートポインタ・標準コンテナに吸収
レビューアーはnew/delete露出箇所を読み取り、集中管理領域へ寄せ切れているか確認する役割を持ちます。
なぜこれをレビューするのか
new/deleteがコード全体に散乱すると以下の設計崩壊を誘発します。
- メモリ解放責務が分散 → 管理不能化
- 例外発生時リーク → 安全性破綻
- テスト容易性低下
- 読解コスト肥大化
レビュー段階で
「new/deleteがどこに現れているか」
を静的に全スコープで読み切る力がレビューアーの品質を左右します。
レビューアー視点
- new箇所を完全に洗い出す
- delete箇所が複数箇所に存在していないか確認
- スマートポインタ移行可能性を確認
- コンストラクタ集中に設計寄せできているか確認
- 例外安全性を崩していないか評価
開発者視点
- new/deleteは設計責務封じ込めの最上位に置く
- スマートポインタ・コンテナへ極力移行
- 解放責任はスコープ管理に寄せる
- 例外安全性を自然保証できる構造に設計する
良い実装例
想定:APIリクエストログの集中管理型
良い設計例
#include <memory>
#include <vector>
#include <string>
#include <iostream>
struct ApiRequestLog {
int requestId;
std::string endpoint;
std::string clientIp;
int responseCode;
time_t requestedAt;
};
class LogBuffer {
public:
void add(std::unique_ptr<ApiRequestLog> logEntry) {
logs_.push_back(std::move(logEntry));
}
void dump() const {
for (const auto& entry : logs_) {
std::cout << entry->requestId << std::endl;
}
}
private:
std::vector<std::unique_ptr<ApiRequestLog>> logs_;
};
void process() {
auto log = std::make_unique<ApiRequestLog>();
log->requestId = 123;
LogBuffer buffer;
buffer.add(std::move(log));
}良いポイント
- new/delete完全封じ込み
- 寿命責務集中
- 例外安全自然保証
- API契約明示化(所有権移譲)
レビュー観点
- new呼び出し箇所を完全把握できるか
- deleteが複数箇所に分散していないか
- コンストラクタ集中に設計寄せできているか
- スマートポインタ移行余地が残っていないか
- 呼び出し側の解放責務が消滅しているか
- 例外時も寿命保証されているか
良くない実装例: ケース1(delete責務分散)
問題例①
class LogBuffer {
public:
void add(ApiRequestLog* logEntry) {
logs_.push_back(logEntry);
}
void cleanup() {
for (auto* entry : logs_) {
delete entry;
}
logs_.clear();
}
private:
std::vector<ApiRequestLog*> logs_;
};
@Reviewerdelete責務がクラス内に分散しています。スマートポインタを導入し、所有権移譲をadd()引数型で明示してください。cleanup()も不要になります。
問題点
- 解放責務分散
- 保守コスト上昇
- 例外時安全性不保証
改善例
改善例①
void add(std::unique_ptr<ApiRequestLog> logEntry) {
logs_.push_back(std::move(logEntry));
}良くない実装例: ケース2(コンストラクタ集中欠如)
問題例②
class Session {
public:
void start() {
log_ = new ApiRequestLog();
}
void end() {
delete log_;
}
private:
ApiRequestLog* log_;
};
@Reviewernew/deleteはコンストラクタとデストラクタに集中させてください。可能ならunique_ptr導入で自動解放に統合してください。
改善例
改善例②
class Session {
public:
Session() : log_(std::make_unique<ApiRequestLog>()) {}
private:
std::unique_ptr<ApiRequestLog> log_;
};良くない実装例: ケース3(スマートポインタ未導入)
問題例③
ApiRequestLog* log = new ApiRequestLog();
// 処理略
delete log;
@Reviewernew/deleteは基本的にコード露出を排除してください。スマートポインタまたはスコープ自動管理構造に統一移行してください。
改善例
改善例③
auto log = std::make_unique<ApiRequestLog>();観点チェックリスト
まとめ
new/delete集中管理レビューは
「破棄責任を構造上読める設計に仕上げるレビュー」です。
レビューアーは常に
- new呼び出しはどこに存在するか?
- delete責務は集中管理されているか?
- 自動解放設計に寄せ切れているか?
を静的に読み取り、RAII徹底 → 呼び出し側負荷ゼロ化設計へ誘導するのが役割です。
