この記事のポイント

  • 裸配列(raw array)をレビューアーがどのように読み解くべきか整理
  • vector移行を含めた裸配列使用最小化指針を整理
  • 安全設計を実現する所有権集中と責務整理レビューができる

そもそも裸配列とは

C++における裸配列(raw array)は以下のような形式です。

int arr[10];          // 固定長ローカル配列
int* arr = new int[10];  // 動的確保配列

C++では長年、この裸配列が一般的に使われてきました。
しかし、以下の特徴から設計上の負債になりやすいです。

  • 境界管理が呼び出し側責任
  • delete[]忘れリスク
  • 例外安全性崩壊
  • 所有権責務が曖昧化

C++11以降のモダンC++設計では裸配列の利用は極力最小限に抑えるのが設計標準です。

現代C++の裸配列原則
  • std::vectorまたはstd::arrayへ置き換え可能なら置き換える
  • 低レベルAPIとの境界以外で裸配列は原則使用しない
  • 裸配列使用時は必ず責務集中構造で管理する

レビューアーは裸配列が残存していないか確認し、不要な場合は排除提案する役割を担います。

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

裸配列は以下の設計事故を頻発させます。

  • 配列長の分散管理
  • 境界外アクセスの静的検出困難
  • delete[]忘れや二重delete
  • 例外安全性の破壊
  • API契約の読解困難化

レビューアーはコードの長期保守性・安全性の観点から構造的排除を誘導する必要があります。

レビューアー視点

  • どの裸配列が標準コンテナ移行可能かを判定
  • 動的確保配列を静的管理構造へ移行提案
  • 所有権移譲の明示化を促進
  • API契約における配列責務整理を確認

開発者視点

  • new/delete[]排除を前提に設計する
  • vector/std::array適用箇所を最大化する
  • ポインタ+サイズ契約をvector契約に変換する
  • 裸配列使用箇所は極小限定(C-API境界等)

良い実装例

想定:APIリクエストログ管理を裸配列最小化

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

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

class LogBuffer {
public:
    explicit LogBuffer(size_t capacity)
        : logs_(capacity) {}

    void add(const ApiRequestLog& logEntry) {
        logs_.push_back(logEntry);
    }

    void dump() const {
        for (const auto& log : logs_) {
            std::cout << log.requestId << std::endl;
        }
    }

private:
    std::vector<ApiRequestLog> logs_;
};

良いポイント

  • 完全に裸配列排除
  • delete[]責務消滅
  • 境界外アクセス保護
  • 例外安全性自然保証

レビュー観点

  • 配列利用箇所でvector移行可能箇所が残っていないか
  • 動的確保配列をnew/delete[]から隔離しているか
  • API契約も裸配列依存を排除できているか
  • スコープ管理構造でライフサイクル責務を閉じ込めているか
  • 例外安全が構造保証できているか

良くない実装例: ケース1(固定長裸配列)

問題例①
void process() {
    ApiRequestLog logs[10];

    logs[0].requestId = 1;
}
@Reviewer
固定長配列はstd::vectorまたはstd::arrayへ移行してください。サイズ管理責務をコードから排除し、より安全なスコープ管理構造に寄せましょう。

問題点

  • サイズ管理が暗黙的
  • 拡張性がない
  • 境界違反が静的検出困難

改善例

改善例①
std::vector<ApiRequestLog> logs;
logs.push_back({1, "/api/items", "127.0.0.1", 200, std::time(nullptr)});

良くない実装例: ケース2(動的裸配列)

問題例②
ApiRequestLog* logs = new ApiRequestLog[10];
logs[0].requestId = 1;
delete[] logs;
@Reviewer
new/delete[]を排除し、vector移行してください。例外安全性・ライフサイクル集中管理が保証されます。

問題点

  • delete[]忘れリスク
  • 例外時リーク
  • 呼び出し側に所有権転嫁

改善例

改善例②
std::vector<ApiRequestLog> logs(10);
logs[0].requestId = 1;

良くない実装例: ケース3(API契約裸配列露出)

問題例③
void save(ApiRequestLog* logs, size_t count) {
    for (size_t i = 0; i < count; ++i) {
        std::cout << logs[i].requestId << std::endl;
    }
}
@Reviewer
API契約もvector受取にリファクタリングしてください。裸配列+サイズ契約は分散責務となり、vectorなら一括管理可能です。

改善例

改善例③
void save(const std::vector<ApiRequestLog>& logs) {
    for (const auto& entry : logs) {
        std::cout << entry.requestId << std::endl;
    }
}

観点チェックリスト


まとめ

裸配列レビューは
「責務分散を責務集中に読み替える作業」です。

レビューアーは

  • まだ残っている裸配列はどこか?
  • スマートポインタ・vector移行は可能か?
  • API契約まで移行できる構造か?

を常に読み解き、設計全体の安全領域へ押し上げる判断力が必要になります。

UML Diagram