メモリベストプラクティス

安全で効率的なメモリ管理。

概要

C++でのメモリ管理は、プログラムの品質を左右する重要な要素です。 ベストプラクティスに従うことで、バグを減らします。

ルール1:raw ポインタを避ける

反例:手動メモリ管理

// 避けるべき
int* ptr = new int[100];
// ... 使用 ...
delete[] ptr;  // 忘却の危険性

推奨:スマートポインタ使用

// 推奨
auto ptr = std::make_unique<int[]>(100);
// ... 使用 ...
// 自動破棄

ルール2:スタック変数を優先

ヒープ割り当てより、スタック割り当ての方が パフォーマンスと安全性に優れています。

#include <iostream>
#include <vector>

int main() {
    // 推奨:スタック上に配列
    std::vector<int> arr(100);
    arr[0] = 42;
    
    // 自動破棄
    
    // 代わりに、ヒープ割り当ては:
    // auto ptr = std::make_unique<std::vector<int>>(100);
    
    return 0;
}

ルール3:RAII を活用

リソースを自動で管理する RAII を活用することで、 例外やプログラム異常時にも安全です。

#include <iostream>
#include <fstream>

void safeFileOperation() {
    // RAII: ファイルは自動的に閉じられる
    std::ofstream file("data.txt");
    
    if (!file) {
        throw std::runtime_error("ファイル開け失敗");
    }
    
    file << "Data..." << std::endl;
    
    // スコープを抜ける際に自動破棄
}

int main() {
    try {
        safeFileOperation();
    } catch (...) {
        std::cout << "エラー処理" << std::endl;
    }
    
    return 0;
}

ルール4:const 参照でコピーを避ける

#include <iostream>
#include <string>
#include <vector>

// 避けるべき:値渡しでコピー
void processDataBad(std::vector<int> data) {
    for (int x : data) {
        std::cout << x << " ";
    }
}

// 推奨:const参照で効率化
void processDataGood(const std::vector<int>& data) {
    for (int x : data) {
        std::cout << x << " ";
    }
}

int main() {
    std::vector<int> data = {1, 2, 3, 4, 5};
    
    processDataGood(data);  // コピーなし、効率的
    
    return 0;
}

ルール5:unique_ptr をデフォルトに

#include <iostream>
#include <memory>
#include <vector>

class Node {
public:
    int value;
    std::unique_ptr<Node> next;
    
    Node(int v) : value(v), next(nullptr) {}
};

int main() {
    auto head = std::make_unique<Node>(1);
    head->next = std::make_unique<Node>(2);
    head->next->next = std::make_unique<Node>(3);
    
    // リンクリスト全体が自動破棄される
    
    return 0;
}

複数所有が必須な場合のみ shared_ptr を使う。

ルール6:メモリリークをチェック

Development 中は、メモリリーク検出ツール(例:Valgrind、AddressSanitizer) を使用すべきです。

# AddressSanitizer を使用
g++ -g -fsanitize=address program.cpp -o program
./program

これにより、メモリ関連のバグを早期に発見できます。

ルール7:パフォーマンスとの勘案

スマートポインタには若干のオーバーヘッドがあります。 ultra-high-performance

#include <iostream>
#include <memory>
#include <chrono>

int main() {
    auto start = std::chrono::high_resolution_clock::now();
    
    for (int i = 0; i < 1000000; i++) {
        auto ptr = std::make_unique<int>(i);
    }
    
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    
    std::cout << "時間: " << duration.count() << "ms" << std::endl;
    
    return 0;
}

メモリ管理チェックリスト

  • [ ] raw new/delete を避けた
  • [ ] スマートポインタを使用した
  • [ ] RAII 原則を適用した
  • [ ] const参照でコピーを削減した
  • [ ] メモリリークをテストした
  • [ ] パフォーマンス測定を行った
  • [ ] ドキュメント所有権を明示した

ポイント

  • raw ポインタを避ける
  • スタック変数を優先
  • RAII パターンを活用
  • unique_ptr をデフォルト
  • テストツールでメモリ検証

まとめ

  • メモリ管理は C++の基礎設計哲学
  • Modern C++では自動管理が基本
  • 手動管理は慎重に、ドキュメント必須