vectorの基礎知識とメリット
動的配列としてのvectorの特徴
C++のvectorは、最も広く使用されている標準テンプレートライブラリ(STL)のコンテナの1つです。その本質は動的配列にあり、以下の特徴を持っています:
- 自動的なメモリ管理
- 要素数に応じて自動的にメモリサイズを調整
- メモリの確保・解放を自動的に行う
- 配列のリサイズが必要な場合は内部で自動的に処理
// vectorの基本的な使用例 #include <vector> std::vector<int> numbers; // 空のvectorを作成 numbers.push_back(1); // 要素を追加すると自動的にメモリを確保 numbers.push_back(2); // メモリは自動的に管理され、スコープを抜けると解放される
- 連続したメモリ領域
- 要素が物理的に連続して配置される
- キャッシュ効率が高い
- ポインタ演算が可能
std::vector<int> arr = {1, 2, 3, 4, 5}; int* ptr = arr.data(); // 連続したメモリへの直接アクセスが可能 std::cout << *(ptr + 2); // 3番目の要素にアクセス
- テンプレートベース
- あらゆる型のデータを格納可能
- カスタムクラスも利用可能
- 型安全性が保証される
vectorが選ばれる3つの理由
- 使いやすさとパフォーマンスのバランス
// 簡単な初期化と操作 std::vector<std::string> names = {"Alice", "Bob", "Charlie"}; names.push_back("David"); // 末尾に追加 names.pop_back(); // 末尾から削除
- 豊富な機能セット
- イテレータによる要素へのアクセス
- 容量の制御機能
- 要素の検索・ソート機能
std::vector<int> nums = {3, 1, 4, 1, 5}; std::sort(nums.begin(), nums.end()); // 要素のソート auto it = std::find(nums.begin(), nums.end(), 4); // 要素の検索
- STLアルゴリズムとの親和性
- 標準アルゴリズムとシームレスに連携
- 高度な操作が簡単に実装可能
- パフォーマンスの最適化が容易
配列やリストと比較したvectorの優位性
特徴 | vector | 配列 | list |
---|---|---|---|
メモリ管理 | 自動 | 手動 | 自動 |
サイズ変更 | 可能 | 不可能 | 可能 |
メモリ効率 | 高 | 最高 | 低 |
要素アクセス | O(1) | O(1) | O(n) |
挿入/削除(末尾) | O(1)* | N/A | O(1) |
挿入/削除(中間) | O(n) | N/A | O(1) |
*: 再割り当てが必要な場合を除く
vectorの実践的な優位性:
- パフォーマンス面での利点
// キャッシュフレンドリーな操作 std::vector<int> vec(10000); // 連続したメモリ領域による高速なイテレーション for(const auto& element : vec) { // キャッシュヒット率が高い操作 }
- メモリ管理の簡便さ
void process_data() { std::vector<double> data; // スコープを抜けると自動的にメモリ解放 // メモリリークの心配が不要 }
- 柔軟性と安全性
std::vector<int> safe_vec; safe_vec.reserve(1000); // メモリの事前確保 // 範囲チェック付きのアクセス try { int val = safe_vec.at(5); // 範囲外アクセスは例外をスロー } catch (const std::out_of_range& e) { // 安全に例外をハンドリング }
このように、vectorは現代のC++プログラミングにおいて、安全性、使いやすさ、パフォーマンスのバランスが取れたコンテナとして、幅広い用途で活用されています。
vectorの基本操作マスターガイド
要素の追加と削除を完全に理解する
vectorの要素操作は、効率的なプログラミングの基礎となります。以下に主要な操作方法を説明します。
1. 要素の追加
#include <vector> #include <string> std::vector<std::string> fruits; // 末尾への要素追加 fruits.push_back("apple"); // 最も一般的な追加方法 fruits.emplace_back("banana"); // 直接構築による効率的な追加 // 指定位置への要素追加 auto it = fruits.begin(); fruits.insert(it, "orange"); // 先頭に追加 fruits.emplace(it + 1, "grape"); // 2番目の位置に追加 // 複数要素の追加 std::vector<std::string> more_fruits = {"mango", "kiwi"}; fruits.insert(fruits.end(), more_fruits.begin(), more_fruits.end());
2. 要素の削除
// 末尾要素の削除 fruits.pop_back(); // 指定位置の要素を削除 auto it = fruits.begin() + 2; fruits.erase(it); // 3番目の要素を削除 // 範囲削除 fruits.erase(fruits.begin(), fruits.begin() + 2); // 最初の2要素を削除 // 条件に基づく削除(remove-erase イディオム) auto remove_it = std::remove_if(fruits.begin(), fruits.end(), [](const std::string& s) { return s.length() < 5; }); fruits.erase(remove_it, fruits.end());
イテレーターを使った効率的な操作方法
イテレータは、vectorの要素に対する強力なアクセス手段を提供します。
std::vector<int> numbers = {1, 2, 3, 4, 5}; // 基本的なイテレータの使用 for (auto it = numbers.begin(); it != numbers.end(); ++it) { std::cout << *it << " "; } // 逆順イテレータ for (auto rit = numbers.rbegin(); rit != numbers.rend(); ++rit) { std::cout << *rit << " "; } // 範囲ベースのfor文(内部的にイテレータを使用) for (const auto& num : numbers) { std::cout << num << " "; }
イテレータを活用した高度な操作
std::vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3}; // ソートと二分探索 std::sort(vec.begin(), vec.end()); auto it = std::lower_bound(vec.begin(), vec.end(), 4); // 4以上の最初の要素 // 要素の変換 std::transform(vec.begin(), vec.end(), vec.begin(), [](int n) { return n * 2; }); // 部分的な操作 auto mid = vec.begin() + vec.size() / 2; std::rotate(vec.begin(), mid, vec.end()); // ベクトルを中点で回転
メモリ管理のベストプラクティス
効率的なメモリ管理は、vectorのパフォーマンスを最適化する上で重要です。
1. 容量の事前確保
std::vector<int> efficient_vector; efficient_vector.reserve(1000); // 1000要素分のメモリを事前確保 // 再割り当てが発生しないため効率的 for (int i = 0; i < 1000; ++i) { efficient_vector.push_back(i); } // 容量と大きさの確認 std::cout << "Size: " << efficient_vector.size() << "\n"; std::cout << "Capacity: " << efficient_vector.capacity() << "\n";
2. メモリの最適化
std::vector<int> vec = {1, 2, 3, 4, 5}; vec.shrink_to_fit(); // 未使用のメモリを解放 // 不要になったベクトルのメモリ解放 { std::vector<int>().swap(vec); // メモリを完全に解放 }
3. メモリ効率を考慮した要素追加
class HeavyObject { // 大きなメンバーを持つクラス }; std::vector<HeavyObject> objects; objects.reserve(100); // 効率的な要素追加 for (int i = 0; i < 100; ++i) { objects.emplace_back(); // コピーを避けて直接構築 }
メモリ管理のベストプラクティス一覧
操作 | 推奨される使用法 | 避けるべき使用法 |
---|---|---|
要素追加 | reserve()による事前確保 | 頻繁な push_back() |
メモリ解放 | shrink_to_fit()の適切な使用 | 不必要なresize() |
要素構築 | emplace_back()の活用 | 過度なコピー操作 |
容量管理 | 適切なcapacity()の監視 | 無計画な拡張 |
これらの基本操作と管理手法を適切に組み合わせることで、効率的でメモリ性能の高いプログラムを作成することができます。特に大規模なデータを扱う場合や、パフォーマンスが重要な場面では、これらの手法を意識的に活用することが重要です。
パフォーマンスを最大化する戦略テクニック
reserve() を使ったメモリ最適化
メモリの再割り当ては、vectorのパフォーマンスに大きな影響を与える要因の一つです。reserve()を適切に使用することで、この問題を効果的に解決できます。
メモリ再割り当ての影響
#include <vector> #include <chrono> #include <iostream> // パフォーマンス計測用の関数 template<typename Func> double measure_time(Func f) { auto start = std::chrono::high_resolution_clock::now(); f(); auto end = std::chrono::high_resolution_clock::now(); return std::chrono::duration<double, std::milli>(end - start).count(); } // reserve()なしの場合 double without_reserve() { return measure_time([]() { std::vector<int> vec; for (int i = 0; i < 100000; ++i) { vec.push_back(i); // 容量が不足すると再割り当てが発生 } }); } // reserve()使用の場合 double with_reserve() { return measure_time([]() { std::vector<int> vec; vec.reserve(100000); // 事前に必要な容量を確保 for (int i = 0; i < 100000; ++i) { vec.push_back(i); } }); }
最適化のベストプラクティス
// 効率的なvector初期化 std::vector<int> optimize_vector(size_t expected_size) { std::vector<int> vec; vec.reserve(expected_size); // メモリの事前確保 // 容量と使用量の監視 size_t current_capacity = vec.capacity(); double usage_ratio = static_cast<double>(vec.size()) / current_capacity; return vec; }
emplace_back() で効率的に要素を追加する
emplace_back()は、オブジェクトを直接構築することで、不要なコピーや移動を避けることができます。
class ExpensiveObject { public: ExpensiveObject(int data, std::string name) : data_(data), name_(std::move(name)) {} private: int data_; std::string name_; }; // 非効率的な方法 void inefficient_addition() { std::vector<ExpensiveObject> objects; objects.push_back(ExpensiveObject(42, "test")); // 一時オブジェクトの生成とムーブが発生 } // 効率的な方法 void efficient_addition() { std::vector<ExpensiveObject> objects; objects.emplace_back(42, "test"); // 直接構築 }
パフォーマンス比較
template<typename Container> void compare_insertion_methods() { // push_back vs emplace_back のパフォーマンス比較 Container push_container; auto push_time = measure_time([&]() { for (int i = 0; i < 100000; ++i) { push_container.push_back(ExpensiveObject(i, "test")); } }); Container emplace_container; auto emplace_time = measure_time([&]() { for (int i = 0; i < 100000; ++i) { emplace_container.emplace_back(i, "test"); } }); std::cout << "Push back time: " << push_time << "ms\n"; std::cout << "Emplace back time: " << emplace_time << "ms\n"; }
不要なコピーを減らすテクニック
1. 参照による要素アクセス
std::vector<std::string> strings = {"long string", "another long string"}; // 非効率的なアクセス for (std::string str : strings) { // 各要素がコピーされる // 処理 } // 効率的なアクセス for (const std::string& str : strings) { // 参照によるアクセス // 処理 }
2. 移動セマンティクスの活用
std::vector<std::string> create_and_fill() { std::vector<std::string> result; result.reserve(1000); for (int i = 0; i < 1000; ++i) { std::string temp = "string " + std::to_string(i); result.push_back(std::move(temp)); // 移動によるコピー削減 } return result; // RVO(Return Value Optimization)が適用される }
3. データ構造の最適化
// メモリレイアウトを最適化した構造体 struct OptimizedStruct { // キャッシュライン(64バイト)を考慮したメンバー配置 std::array<int, 16> frequently_accessed_data; // 64バイト std::string rarely_accessed_data; // 別のキャッシュライン }; std::vector<OptimizedStruct> optimized_vector;
パフォーマンス最適化チェックリスト
最適化ポイント | 実装方法 | 期待される効果 |
---|---|---|
メモリ予約 | reserve() | 再割り当ての削減 |
要素構築 | emplace_back() | コピー/ムーブの削減 |
要素アクセス | const参照 | 不要なコピーの防止 |
メモリ管理 | shrink_to_fit() | メモリ使用量の最適化 |
データ構造 | アライメント考慮 | キャッシュ効率の向上 |
これらの最適化テクニックを適切に組み合わせることで、vectorを使用したプログラムのパフォーマンスを大幅に向上させることができます。ただし、過度な最適化は可読性を損なう可能性があるため、実際のユースケースに応じて適切なバランスを取ることが重要です。
実務での活用事例と応用テクニック
大量データ処理での効率的な使い方方法
大規模なデータを扱う際の効率的なvectorの使用方法を解説します。
1. データバッチ処理の実装
#include <vector> #include <algorithm> #include <numeric> class DataProcessor { public: // バッチサイズを指定して初期化 DataProcessor(size_t batch_size) : batch_size_(batch_size) { buffer_.reserve(batch_size); } // データの追加と自動バッチ処理 void add_data(const double value) { buffer_.push_back(value); if (buffer_.size() >= batch_size_) { process_batch(); } } // 残りのデータを処理 void flush() { if (!buffer_.empty()) { process_batch(); } } private: void process_batch() { // バッチ処理のロジック double sum = std::accumulate(buffer_.begin(), buffer_.end(), 0.0); double average = sum / buffer_.size(); // 処理結果を保存 results_.push_back(average); // バッファをクリア buffer_.clear(); } std::vector<double> buffer_; std::vector<double> results_; size_t batch_size_; };
2. 効率的なデータフィルタリング
template<typename T> class DataFilter { public: // フィルタリング条件を適用 std::vector<T> filter(const std::vector<T>& data, std::function<bool(const T&)> predicate) { std::vector<T> filtered; filtered.reserve(data.size()); // 最悪のケースに備えて予約 std::copy_if(data.begin(), data.end(), std::back_inserter(filtered), predicate); filtered.shrink_to_fit(); // 未使用のメモリを解放 return filtered; } };
マルチスレッド環境での安全な操作
1. スレッドセーフなvectorラッパー
#include <mutex> #include <shared_mutex> template<typename T> class ThreadSafeVector { public: void push_back(const T& value) { std::unique_lock<std::shared_mutex> lock(mutex_); data_.push_back(value); } bool empty() const { std::shared_lock<std::shared_mutex> lock(mutex_); return data_.empty(); } size_t size() const { std::shared_lock<std::shared_mutex> lock(mutex_); return data_.size(); } // 範囲ベースのfor文のサポート auto begin() { std::shared_lock<std::shared_mutex> lock(mutex_); return data_.begin(); } auto end() { std::shared_lock<std::shared_mutex> lock(mutex_); return data_.end(); } private: std::vector<T> data_; mutable std::shared_mutex mutex_; };
2. 並列処理での活用例
#include <thread> #include <future> template<typename T> class ParallelProcessor { public: // 並列データ処理 std::vector<T> process_parallel(const std::vector<T>& input, std::function<T(const T&)> processor, size_t thread_count) { std::vector<T> result(input.size()); std::vector<std::future<void>> futures; size_t chunk_size = input.size() / thread_count; for (size_t i = 0; i < thread_count; ++i) { size_t start = i * chunk_size; size_t end = (i == thread_count - 1) ? input.size() : (i + 1) * chunk_size; futures.push_back(std::async(std::launch::async, [&, start, end]() { for (size_t j = start; j < end; ++j) { result[j] = processor(input[j]); } })); } // すべてのスレッドの完了を待機 for (auto& future : futures) { future.wait(); } return result; } };
カスタムアロケータの実装と活用
1. プールアロケータの実装
template<typename T> class PoolAllocator { public: using value_type = T; PoolAllocator(size_t pool_size) : pool_size_(pool_size) { memory_pool_ = std::malloc(pool_size * sizeof(T)); free_blocks_.reserve(pool_size); for (size_t i = 0; i < pool_size; ++i) { free_blocks_.push_back(static_cast<T*>(memory_pool_) + i); } } ~PoolAllocator() { std::free(memory_pool_); } T* allocate(size_t n) { if (n > free_blocks_.size()) { throw std::bad_alloc(); } T* ptr = free_blocks_.back(); free_blocks_.pop_back(); return ptr; } void deallocate(T* p, size_t n) { free_blocks_.push_back(p); } private: void* memory_pool_; std::vector<T*> free_blocks_; size_t pool_size_; };
2. カスタムアロケータの使用例
// カスタムアロケータを使用したvector template<typename T> using PoolVector = std::vector<T, PoolAllocator<T>>; // 使用例 void use_pool_vector() { PoolAllocator<int> allocator(1000); PoolVector<int> numbers(allocator); numbers.reserve(100); for (int i = 0; i < 100; ++i) { numbers.push_back(i); } }
実装のベストプラクティス一覧
シナリオ | 推奨される実装 | 主な利点 |
---|---|---|
大量データ処理 | バッチ処理 | メモリ効率の向上 |
マルチスレッド | スレッドセーフラッパー | データ競合の防止 |
メモリ最適化 | カスタムアロケータ | アロケーション効率の向上 |
並列処理 | 並列プロセッサ | 処理速度の向上 |
これらの実装例は、実際の開発現場でよく遭遇する課題に対する解決策を提供します。状況に応じて適切な実装を選択し、必要に応じてカスタマイズすることで、効率的で安全なコードを作成することができます。
よくあるバグと対処法
範囲外アクセスを防ぐベストプラクティス
1. 範囲外アクセスの一般的なケース
#include <vector> #include <stdexcept> class VectorSafeAccess { public: static void demonstrate_common_errors() { std::vector<int> numbers = {1, 2, 3}; try { // 危険な操作例1: 境界チェックなしの添字アクセス int value = numbers[5]; // 未定義動作 // 安全な操作例1: at()メソッドの使用 value = numbers.at(5); // std::out_of_range例外をスロー } catch (const std::out_of_range& e) { // 例外処理 } // 危険な操作例2: 空のvectorへのアクセス std::vector<int> empty_vec; // front()やback()を呼ぶ前に必ずチェック if (!empty_vec.empty()) { int first = empty_vec.front(); } } };
2. 安全なイテレーション
class SafeIteration { public: template<typename T> static void safe_iterate(const std::vector<T>& vec) { // 範囲ベースforループ(最も安全) for (const auto& element : vec) { // 範囲外アクセスの心配なし } // イテレータを使用する場合の安全なパターン for (auto it = vec.begin(); it != vec.end(); ++it) { // イテレータの有効性を確認 if (it != vec.end()) { // 安全なアクセス } } } // 境界チェック用のユーティリティ関数 template<typename T> static bool is_valid_index(const std::vector<T>& vec, size_t index) { return index < vec.size(); } };
メモリリークを防ぐための注意点
1. スマートポインタの活用
#include <memory> class ResourceManager { public: // メモリリークを防ぐための構造 struct Resource { std::vector<std::unique_ptr<int>> data; void add_data(int value) { // スマートポインタを使用してメモリ管理を自動化 data.push_back(std::make_unique<int>(value)); } }; // リソースの安全な管理 static void manage_resources() { std::vector<std::shared_ptr<Resource>> resources; // リソースの追加 resources.push_back(std::make_shared<Resource>()); // スコープを抜けると自動的にクリーンアップ } };
2. RAII原則の適用
class RAIIExample { public: class ScopedResource { public: ScopedResource() { // リソースの確保 data_.reserve(initial_capacity); } ~ScopedResource() { // 自動クリーンアップ data_.clear(); data_.shrink_to_fit(); } private: std::vector<int> data_; static const size_t initial_capacity = 1000; }; static void use_scoped_resource() { ScopedResource resource; // リソースの使用 // スコープを抜けると自動的にクリーンアップ } };
イテレータ無効化の罠と対策
1. イテレータ無効化の典型的なケース
class IteratorInvalidation { public: static void demonstrate_invalidation() { std::vector<int> numbers = {1, 2, 3, 4, 5}; // 危険なパターン for (auto it = numbers.begin(); it != numbers.end(); ++it) { if (*it == 3) { numbers.push_back(6); // イテレータが無効化される // 以降の操作は未定義動作 } } // 安全なパターン for (size_t i = 0; i < numbers.size(); ++i) { if (numbers[i] == 3) { numbers.push_back(6); // インデックスは無効化されない } } } };
2. イテレータの安全な更新
class SafeIteratorUpdate { public: template<typename T> static void safe_modification(std::vector<T>& vec) { // 変更が必要な要素のインデックスを記録 std::vector<size_t> indices_to_modify; for (size_t i = 0; i < vec.size(); ++i) { if (需要_modification(vec[i])) { indices_to_modify.push_back(i); } } // 記録したインデックスを使用して安全に更新 for (auto index : indices_to_modify) { if (index < vec.size()) { // 範囲チェック modify_element(vec[index]); } } } private: template<typename T> static bool needs_modification(const T& element) { // 修正が必要かどうかの判定ロジック return true; } template<typename T> static void modify_element(T& element) { // 要素の修正ロジック } };
バグ防止チェックリスト
バグの種類 | 予防方法 | 検出方法 |
---|---|---|
範囲外アクセス | at()の使用、境界チェック | 例外処理、アサーション |
メモリリーク | スマートポインタ、RAII | メモリチェッカー |
イテレータ無効化 | インデックス使用、参照保持 | コードレビュー、静的解析 |
デバッグのベストプラクティス
- 事前条件の検証
template<typename T> void safe_vector_operations(std::vector<T>& vec) { assert(!vec.empty() && "Vector must not be empty"); // 操作の実行 }
- 例外安全性の確保
template<typename T> void exception_safe_push(std::vector<T>& vec, const T& value) { try { vec.push_back(value); } catch (const std::bad_alloc& e) { // メモリ確保失敗の処理 handle_allocation_failure(); } }
- デバッグ情報の出力
#include <iostream> void vector_state_debug(const std::vector<int>& vec) { std::cout << "Size: " << vec.size() << ", Capacity: " << vec.capacity() << std::endl; // その他の状態情報 }
これらの対策を適切に実装することで、多くの一般的なバグを防ぎ、より安全なコードを作成することができます。
次のステップ:照合最適化と応用
STLアルゴリズムとの組み合わせ活用法
標準ライブラリのアルゴリズムとvectorを組み合わせることで、効率的で表現力豊かなコードを書くことができます。
1. 高度な検索と照合
#include <algorithm> #include <vector> #include <numeric> class AdvancedSearch { public: // 二分探索を使用した効率的な検索 template<typename T> static bool binary_search_optimized(const std::vector<T>& vec, const T& target) { // 事前条件:ベクトルがソート済みであること assert(std::is_sorted(vec.begin(), vec.end()) && "Vector must be sorted for binary search"); return std::binary_search(vec.begin(), vec.end(), target); } // 部分一致検索の実装 template<typename T> static std::vector<size_t> partial_match(const std::vector<T>& vec, const std::vector<T>& pattern) { std::vector<size_t> matches; auto it = vec.begin(); while ((it = std::search(it, vec.end(), pattern.begin(), pattern.end())) != vec.end()) { matches.push_back(std::distance(vec.begin(), it)); ++it; } return matches; } };
2. 効率的な変換と加工
class VectorTransformation { public: // 並列処理を活用した変換 template<typename T, typename Func> static std::vector<T> parallel_transform(const std::vector<T>& input, Func transformer) { std::vector<T> result(input.size()); #pragma omp parallel for for (size_t i = 0; i < input.size(); ++i) { result[i] = transformer(input[i]); } return result; } // 集約処理の最適化 template<typename T> static T optimized_accumulate(const std::vector<T>& vec) { return std::reduce(std::execution::par, vec.begin(), vec.end(), T{}); } };
カスタムクラスでvectorを使用する際の注意点
1. 効率的なカスタムクラスの実装
class OptimizedCustomClass { public: OptimizedCustomClass(int data = 0) : data_(data) {} // ムーブコンストラクタの最適化 OptimizedCustomClass(OptimizedCustomClass&& other) noexcept : data_(std::exchange(other.data_, 0)) {} // ムーブ代入演算子の最適化 OptimizedCustomClass& operator=(OptimizedCustomClass&& other) noexcept { if (this != &other) { data_ = std::exchange(other.data_, 0); } return *this; } // 比較演算子の実装(STLアルゴリズム用) bool operator<(const OptimizedCustomClass& other) const { return data_ < other.data_; } private: int data_; }; // カスタムアロケータとの組み合わせ template<typename T> class CustomAllocator { public: using value_type = T; T* allocate(std::size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); } void deallocate(T* p, std::size_t n) { ::operator delete(p); } };
2. パフォーマンス最適化テクニック
class PerformanceOptimizations { public: // メモリレイアウトの最適化 struct CacheOptimizedStruct { // キャッシュラインに合わせた配置 alignas(64) std::array<int, 16> hot_data; std::string cold_data; }; // ベクトル操作の最適化 static void optimize_vector_operations() { std::vector<CacheOptimizedStruct> vec; vec.reserve(1000); // メモリ再割り当ての回避 // バッチ処理による効率化 std::vector<CacheOptimizedStruct> batch; batch.reserve(100); for (size_t i = 0; i < 1000; ++i) { batch.push_back(CacheOptimizedStruct{}); if (batch.size() == 100) { // バッチ処理の実行 process_batch(batch); batch.clear(); } } } private: static void process_batch(std::vector<CacheOptimizedStruct>& batch) { // バッチ処理の実装 } };
より高度なデータ構造への発展
1. 2次元ベクトルの最適化
class Advanced2DVector { public: // 効率的な2次元ベクトル実装 template<typename T> class Matrix { public: Matrix(size_t rows, size_t cols) : data_(rows * cols), rows_(rows), cols_(cols) {} T& at(size_t row, size_t col) { return data_[row * cols_ + col]; } const T& at(size_t row, size_t col) const { return data_[row * cols_ + col]; } private: std::vector<T> data_; size_t rows_; size_t cols_; }; // キャッシュ効率の良い走査 template<typename T> static void cache_efficient_traverse(Matrix<T>& matrix) { for (size_t i = 0; i < matrix.rows(); ++i) { for (size_t j = 0; j < matrix.cols(); ++j) { process_element(matrix.at(i, j)); } } } };
2. 特殊化されたコンテナの実装
template<typename T> class SpecializedContainer { public: // 固定サイズベクトル template<size_t N> class StaticVector { public: void push_back(const T& value) { if (size_ < N) { data_[size_++] = value; } } size_t size() const { return size_; } size_t capacity() const { return N; } private: std::array<T, N> data_; size_t size_ = 0; }; // 循環バッファの実装 class CircularBuffer { public: CircularBuffer(size_t capacity) : data_(capacity), head_(0), tail_(0), size_(0) {} void push(const T& value) { data_[tail_] = value; tail_ = (tail_ + 1) % data_.size(); if (size_ < data_.size()) { ++size_; } else { head_ = (head_ + 1) % data_.size(); } } private: std::vector<T> data_; size_t head_, tail_, size_; }; };
発展的な実装のベストプラクティス
実装パターン | 使用シーン | 主な利点 |
---|---|---|
STLアルゴリズム | データ処理 | 最適化された実装 |
カスタムクラス | 特殊なデータ構造 | メモリ効率の向上 |
2次元配列 | 行列演算 | キャッシュ効率の改善 |
特殊コンテナ | 特定用途 | パフォーマンスの最適化 |
これらの高度な実装テクニックを理解し適切に活用することで、より効率的で保守性の高いコードを作成することができます。