メモリベストプラクティス
安全で効率的なメモリ管理。
概要
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++では自動管理が基本
- 手動管理は慎重に、ドキュメント必須