コンテンツにスキップ

C++/コンテナ

出典: フリー教科書『ウィキブックス(Wikibooks)』
< C++

導入[編集]

コンテナとは、複数の要素を格納するためのC++のデータ構造です。プログラミングにおいて、データを効率的に管理し、操作するためにコンテナは不可欠です。C++の標準ライブラリには、さまざまな種類のコンテナが含まれており、それらを適切に使いこなすことが重要です。

配列と動的配列[編集]

配列は、同じ型の要素を連続したメモリ領域に格納するC++の基本的なデータ構造です。配列のサイズは宣言時に指定され、その後変更することができません。一方、動的配列は、実行時に必要なサイズでメモリを確保し、サイズを動的に変更できます。

静的配列の宣言と初期化は、以下のように行います。

int staticArray[]{1, 2, 3, 4, 5};

動的配列の場合、new演算子を使用してメモリを動的に確保します。

int size;
cout << "Enter array size: ";
cin >> size;
int *dynamicArray = new int[size];

動的配列のサイズを変更するには、新しいサイズのメモリを確保し、古い配列の要素を新しい配列にコピーします。以下は、動的配列のサイズを2倍に拡張する関数の例です。

void resizeArray(int* &array, int &size) {
    int newSize = size * 2;
    int *newArray = new int[newSize];
    for (int i = 0; i < size; i++) {
        newArray[i] = array[i];
    }
    delete[] array;
    array = newArray;
    size = newSize;
}
new/delete演算子を使ったメモリ管理の欠点と克服法
レガシーなnewおよびdelete演算子を使用したメモリ管理にはいくつかの欠点があります。これらの欠点には、メモリリーク、二重解放、無効なポインタの使用などが含まれます。これらの問題を解決するために、より安全で効率的なメモリ管理技術が開発されました。その主な代替技術には、スマートポインタ、標準コンテナ、およびカスタムアロケータが含まれます。
スマートポインタ
スマートポインタは、C++11以降で導入された機能であり、動的なメモリ管理を行うための安全な方法を提供します。主なスマートポインタには、std::unique_ptrstd::shared_ptr、およびstd::weak_ptrがあります。
std::unique_ptr
所有権を唯一の所有者に委任し、自動的にメモリを解放します。スコープを抜けるか、所有者がstd::move()を使用して所有権を移動すると、関連するリソースが解放されます。
std::shared_ptr
複数の所有者を許可し、参照カウントに基づいてメモリを解放します。最後の所有者が参照を解放すると、メモリが解放されます。
std::weak_ptr
std::shared_ptrの循環参照問題を解決するための機能を提供します。弱参照であり、所有者にはカウントされません。
標準コンテナ
標準コンテナは、メモリ管理の一部としても機能します。動的なメモリ割り当てと解放をコンテナが自動的に処理します。例えば、std::vectorstd::liststd::mapなどの標準コンテナは、動的なメモリ管理を必要としますが、ユーザーは手動でメモリを解放する必要はありません。
カスタムアロケータ
カスタムアロケータは、C++でのメモリ管理の柔軟性を向上させるための手法です。アロケータは、特定のメモリ管理ポリシーに基づいてメモリの割り当てと解放を行います。カスタムアロケータを使用することで、メモリの割り当てと解放の方法をカスタマイズし、アプリケーションの要件に合わせることができます。

これらの代替技術は、メモリリークや二重解放などの問題を回避し、メモリ管理をより効率的で安全なものにします。特に、スマートポインタは、手動でメモリ解放を行う必要がなく、リソースの解放を自動化する点で便利です。


標準コンテナライブラリ[編集]

C++の標準ライブラリには、さまざまなタイプのコンテナが含まれています。これらは、特定の目的に合わせて設計されており、効率的なデータ管理と操作を提供します。主なカテゴリは以下の通りです。

シーケンスコンテナ
vector
動的配列。ランダムアクセスが高速で、動的なサイズ変更が可能。
array
静的配列。ランダムアクセスが高速で、静的なサイズ変更が不可能。
deque
両端キュー。ベクトルとリストの特性を併せ持つ。
list
双方向リスト。挿入と削除がリストのサイズに依存せずに高速。
forward_list
シングルトンなリスト。
連想コンテナ
set
重複を許さない要素の集合。要素は昇順にソートされる。
map
キーと値のペアを格納する連想配列。キーに基づいて高速な検索が可能。
multiset
重複を許す要素の集合。要素は昇順にソートされる。
multimap
キーに基づいて重複を許す連想配列。
アダプタコンテナ
stack
LIFO(Last In, First Out)構造。スタック操作(push、pop)を提供。
queue
FIFO(First In, First Out)構造。キュー操作(push、pop)を提供。
priority_queue
優先度付きキュー。要素は優先順位に基づいて格納される。

これらのコンテナは、それぞれ異なる目的に使用されますが、共通のインターフェースを持ち、汎用性が高いです。

コンテナは共通の基底クラスから派生してはいない[編集]

C++の標準テンプレートライブラリ(STL)のコンテナには共通の親クラスはありません。C++は多重継承が可能であり、抽象基底クラスを作成することもできますが、STLの設計では共通の基底クラスを持たないことで、テンプレートの柔軟性とパフォーマンスを最適化しています。

ただし、STLのコンテナは共通のインターフェースや機能を提供するために、テンプレートイテレータという概念を使用しています。

共通のインターフェース[編集]

STLのコンテナは共通のインターフェースを持ちますが、これは継承によるものではなく、同じ名前のメンバ関数を持つことで実現されています。以下は、いくつかの共通のメンバ関数です:

  • begin(): コンテナの先頭を指すイテレータを返す。
  • end(): コンテナの末尾の次を指すイテレータを返す。
  • size(): コンテナの要素数を返す。
  • empty(): コンテナが空かどうかを返す。
  • clear(): コンテナの全要素を削除する。
  • insert(): コンテナに要素を挿入する。
  • erase(): コンテナから要素を削除する。

イテレータ[編集]

STLのコンテナはすべてイテレータをサポートしています。イテレータは、ポインタのように動作し、コンテナの要素を順次アクセスするための抽象化されたオブジェクトです。イテレータは以下の種類に分類されます:

  • 入力イテレータ(Input Iterator)
  • 出力イテレータ(Output Iterator)
  • 前方向イテレータ(Forward Iterator)
  • 双方向イテレータ(Bidirectional Iterator)
  • ランダムアクセスイテレータ(Random Access Iterator)

イテレータを用いることで、異なる種類のコンテナでも同じアルゴリズムを適用することができます。たとえば、std::sortアルゴリズムはランダムアクセスイテレータをサポートするコンテナ(std::vectorstd::dequeなど)で使用できます。

ポリモーフィズムの代替手段[編集]

STLコンテナでポリモーフィズムを必要とする場合、継承や仮想関数を使わずに、テンプレートとイテレータを利用したジェネリックプログラミングを採用することが一般的です。これは、コンパイル時に型が確定するため、ランタイムオーバーヘッドを回避し、より高いパフォーマンスを実現するためです。

以下に、共通の基底クラスを持たない一方で共通の操作を提供するコードの例を示します:

#include <array>
#include <iostream>
#include <list>
#include <vector>

template <typename Container>
void printContainer(const Container& c) {
    for (auto&& el : c) {
        std::cout << el << " ";
    }
    std::cout << std::endl;
}

auto main() -> int {
    auto vec = std::vector{1, 2, 3, 4, 5};
    auto lst = std::list{6, 7, 8, 9, 10};
    auto ary = std::array{11, 12, 13, 14, 15};

    printContainer(vec);
    printContainer(lst);
    printContainer(ary);

    return 0;
}

このように、テンプレートを使うことで異なるコンテナタイプに対して共通の操作を行うことができます。

共通のインターフェースと言ってもコンセプトで制約しているのではなくプログラマの責任で共通の意味を持つメソッドを定義しているに過ぎません。

C++のSTLコンテナは共通の親クラスを持っているわけではなく、共通のインターフェースを持っているという点は、明示的なインターフェースや基底クラスの概念とは異なります。STLの設計は、プログラマが共通の意味を持つメソッドを使用することを前提としていますが、それは言語レベルで強制されるものではありません。

これは、C++のテンプレートとイテレータを中心としたジェネリックプログラミングの特徴です。テンプレートを用いることで、コンパイル時に型のチェックが行われ、適切なメソッドが存在しない場合はコンパイルエラーとなります。これにより、実質的に共通のインターフェースを提供していますが、コンセプト(C++20で導入されたConcepts)を使用する前のC++のバージョンでは、これがプログラマの責任であることは事実です。

C++20 Conceptsの導入[編集]

C++20ではConceptsが導入され、テンプレートパラメータに対する制約を明示的に記述することが可能になりました。これにより、テンプレートが要求するインターフェースを明示的に指定することができ、テンプレートの使い勝手と安全性が向上します。

以下に、C++20のConceptsを使ってシーケンスコンテナのようなインターフェースを要求する例を示します:

#include <array>
#include <concepts>
#include <iostream>
#include <list>
#include <vector>

// SequenceContainerコンセプトの定義
template<typename T>
concept SequenceContainer = requires(T a) {
    { a.begin() } -> std::input_iterator;
    { a.end() } -> std::input_iterator;
    { a.size() } -> std::same_as<typename T::size_type>;
    { a.empty() } -> std::same_as<bool>;
};

template<SequenceContainer Container>
void printContainer(const Container& c) {
    for (auto&& el : c) {
        std::cout << el << " ";
    }
    std::cout << std::endl;
}

auto main() -> int {
    auto vec = std::vector{1, 2, 3, 4, 5};
    auto lst = std::list{6, 7, 8, 9, 10};
    auto ary = std::array{11, 12, 13, 14, 15};
    auto tpl = std::tuple{16, 17, 18, 19, 20};

    printContainer(vec);
    printContainer(lst);
    printContainer(ary);
    // printContainer(tpl); これはエラーになる

    return 0;
}

この例では、SequenceContainerコンセプトを定義し、それをテンプレートパラメータの制約として使用しています。これにより、printContainer関数はSequenceContainerコンセプトを満たす型に対してのみ適用されることが保証されます。

コンセプトで制約したメソッドを未実装なクラスを与えた場合
$ clang++ -Wall -g -std=c++23    seqcont2.cc   -o seqcont2
seqcont2.cc:34:5: error: no matching function for call to 'printContainer'
   34 |     printContainer(tpl);
      |     ^~~~~~~~~~~~~~
seqcont2.cc:18:6: note: candidate template ignored: constraints not satisfied [with Container = std::tuple<int, int, int, int, int>]
   18 | void printContainer(const Container& c) {
      |      ^
seqcont2.cc:17:10: note: because 'std::tuple<int, int, int, int, int>' does not satisfy 'SequenceContainer'
   17 | template<SequenceContainer Container>
      |          ^
seqcont2.cc:11:9: note: because 'a.begin()' would be invalid: no member named 'begin' in 'std::tuple<int, int, int, int, int>'
   11 |     { a.begin() } -> std::input_iterator;
      |         ^
1 error generated.

まとめ[編集]

  • C++のSTLコンテナは共通の親クラスを持たず、共通のインターフェースを持つメソッドを提供していますが、それは言語レベルで強制されるものではありません。
  • プログラマの責任で共通の意味を持つメソッドを使用する必要があります。
  • C++20ではConceptsを使ってテンプレートパラメータに対する制約を明示的に記述することが可能になり、安全性と使い勝手が向上しました。

これにより、C++のジェネリックプログラミングはより明確で強力なものとなっています。

コンテナの操作[編集]

コンテナの操作には、要素の追加、削除、検索、およびアクセスが含まれます。これらの操作は、各コンテナの特性に応じて異なるパフォーマンス特性を持ちます。以下では、代表的なコンテナ操作とその実装方法について説明します。

要素の追加と削除[編集]

追加
新しい要素をコンテナに追加するには、push_back(末尾に追加)、push_front(先頭に追加)、またはinsert(指定した位置に挿入)を使用します。例えば、vectorではpush_backメソッドを使用して要素を末尾に追加します。
std::vector<int> vec = {1, 2, 3};
vec.push_back(4); // 4を末尾に追加
削除
コンテナから要素を削除するには、pop_back(末尾の要素を削除)、pop_front(先頭の要素を削除)、またはerase(指定した位置の要素を削除)を使用します。例えば、listではpop_frontメソッドを使用して先頭の要素を削除します。
std::list<int> myList = {1, 2, 3};
myList.pop_front(); // 先頭の要素を削除

要素の検索とアクセス[編集]

検索
コンテナ内の要素を検索するには、findやcountなどのメソッドを使用します。例えば、setではfindメソッドを使用して特定の要素を検索します。
std::set<int> mySet = {1, 2, 3};
auto it = mySet.find(2); // 2を検索
if (it != mySet.end()) {
    std::cout << "Found" << std::endl;
} else {
    std::cout << "Not found" << std::endl;
}
アクセス
コンテナ内の要素にアクセスする方法は、イテレータや添字を使用します。例えば、vectorでは添字を使用して要素に直接アクセスできます。
std::vector<int> vec = {1, 2, 3};
int element = vec[1]; // 2番目の要素にアクセス

イテレーション[編集]

コンテナ内の全ての要素にアクセスするためには、イテレータを使用します。イテレータは、コンテナ内の要素を順番に走査するためのオブジェクトです。例えば、vectorのイテレータを使用して全ての要素を出力する場合は、次のようになります。

std::vector<int> vec = {1, 2, 3};
for (auto it = vec.begin(); it != vec.end(); i++t) {
    std::cout << *it << " ";
}

サイズと空のチェック[編集]

コンテナの要素数や空の状態をチェックするために、sizeメソッドやemptyメソッドを使用します。例えば、vectorが空かどうかをチェックする場合は、次のようになります。

std::vector<int> vec;
if (vec.empty()) {
    std::cout << "Vector is empty" << std::endl;
} else {
    std::cout << "Vector is not empty" << std::endl;
}

これらの操作を使用することで、コンテナ内のデータを効率的に管理し、操作することができます。適切な操作を選択して、問題に応じてコンテナを使いこなしましょう。

コンテナの比較と選択[編集]

異なるタイプのコンテナがありますが、問題の要件やデータの特性に応じて適切なコンテナを選択することが重要です。各コンテナは、特定の操作に対して異なるパフォーマンス特性を持ち、効率的なデータ操作を実現します。以下では、一般的な比較基準とコンテナの選択方法について説明します。

各コンテナの特性と適用例[編集]

vector
動的配列。要素の追加や削除が配列の末尾で行われる場合は高速ですが、途中への挿入や削除はコストが高い場合があります。ランダムアクセスが高速で、要素への直接アクセスが必要な場合に適しています。
list
双方向リスト。要素の追加や削除が高速で、リストのサイズに依存しない操作性を持ちます。ただし、ランダムアクセスが効率的ではないため、要素の添字によるアクセスが頻繁に行われる場合は適していません。
deque
両端キュー。vectorとlistの特性を併せ持ち、先頭と末尾の要素へのアクセスが高速です。要素の追加や削除も効率的ですが、中間への挿入や削除はvectorよりも遅い場合があります。
set
重複を許さない要素の集合。要素は自動的に昇順でソートされます。高速な検索が可能であり、要素の挿入や削除も効率的です。重複を許さないユニークな要素を管理する場合に適しています。
map
キーと値のペアを格納する連想配列。キーに基づいて高速な検索が可能であり、要素の挿入や削除も効率的です。データのキーに基づいて値を取得する必要がある場合に適しています。
stack
LIFO(Last In, First Out)構造。要素の追加と削除がスタック操作(push、pop)によって行われます。スタックの特性が必要な場合に使用されます。
queue
FIFO(First In, First Out)構造。要素の追加と削除がキュー操作(push、pop)によって行われます。キューの特性が必要な場合に使用されます。
priority_queue
優先度付きキュー。要素は優先順位に基づいて格納され、最も優先度が高い要素が先頭に位置します。優先度付きの要素の管理が必要な場合に使用されます。

コンテナの選択方法[編集]

コンテナを選択する際には、次のような基準を考慮することが重要です。

操作の頻度
使用する操作によって、コンテナの選択が異なります。例えば、要素の追加や削除が頻繁に行われる場合は、listやdequeが適しています。一方、ランダムアクセスが必要な場合は、vectorが適しています。
データの特性
扱うデータの特性もコンテナの選択に影響します。例えば、重複を許さないユニークな要素を管理する場合は、setやmapが適しています。データが優先度付きで管理される必要がある場合は、priority_queueを使用します。
メモリ効率
コンテナのメモリ使用量も考慮する必要があります。例えば、vectorは要素の追加時にメモリ再割り当てが発生するため、頻繁な要素の追加が予想される場合は、dequeの使用を検討すると良いでしょう。
計算量
各操作の時間計算量も重要な要素です。例えば、setやmapでは、要素の検索がO(log n)で行われますが、vectorやdequeではO(n)となります。要件に応じて、適切な計算量を選択する必要があります。

これらの基準を考慮して、問題に最適なコンテナを選択することが重要です。効率的なデータ操作を実現するために、適切なコンテナを選択し、適切に活用することが重要です。

コンテナとメモリ安全[編集]

コンテナを使用する際には、メモリ安全性について考慮する必要があります。メモリ安全性とは、プログラムがメモリを適切に管理し、メモリリークや無効なメモリアクセスなどの問題を防止することを指します。以下では、コンテナを安全に使用するためのいくつかのポイントを紹介します。

メモリリークの防止[編集]

動的メモリ割り当てを行う場合、割り当てられたメモリ領域を適切に解放することが重要です。特に、コンテナが大量のメモリを使用する場合は、メモリリークが発生する可能性が高くなります。以下は、動的な配列の場合のメモリ解放の例です。

int* dynamicArray = new int[size];
// dynamicArrayを使用する処理
delete[] dynamicArray; // 割り当てられたメモリを解放

また、スマートポインタを使用することで、メモリリークを回避することもできます。unique_ptrやshared_ptrなどのスマートポインタは、メモリ解放を自動的に行うため、コンテナ内の要素を管理する際に役立ちます。

イテレータの有効性の確認[編集]

コンテナ内の要素を操作する際には、イテレータの有効性を確認することが重要です。無効なイテレータを使用すると、プログラムがクラッシュする可能性があります。特に、要素の追加や削除が行われた後にイテレータを使用する場合は、イテレータが無効になる可能性があるため、注意が必要です。

std::vector<int> vec = {1, 2, 3};
auto it = vec.begin();
vec.push_back(4); // イテレータitは無効になる可能性がある

イテレータが無効になる可能性がある場合は、操作後にイテレータを再取得するか、イテレータの有効性を事前にチェックすることが重要です。

メモリ領域のオーバーフローやアンダーフローの防止[編集]

コンテナに要素を追加する際には、メモリ領域のオーバーフローやアンダーフローを防止する必要があります。特に、添字を使用して要素にアクセスする場合は、配列外アクセスが発生しないように注意する必要があります。

std::vector<int> vec = {1, 2, 3};
int element = vec[10]; // インデックスが範囲外の場合、未定義の動作となる可能性がある

範囲外のアクセスを防止するためには、添字の範囲を事前にチェックするか、atメソッドを使用して安全なアクセスを行うことが推奨されます。

if (index >= 0 && index < vec.size()) {
    int element = vec[index]; // インデックスの範囲をチェックして安全にアクセス
}

コンテナを使用する際には、メモリ安全性について十分な注意を払い、メモリリークや無効なメモリアクセスなどの問題を防止するようにしましょう。安全なコードは、より信頼性が高く、メンテナンスが容易です。

コンテナと範囲ベースのFOR[編集]

C++11から導入された範囲ベースのforループは、コンテナ内の要素を簡潔に走査するための便利な構文です。この構文を使用することで、イテレータを明示的に操作する必要がなくなり、コードがより読みやすくなります。以下では、範囲ベースのforループの使用方法と利点について説明します。

使用方法[編集]

範囲ベースのforループは、以下のような構文を持ちます。

for (auto element : container) {
    // 要素を処理するコード
}

この構文では、container内の各要素がelementに順番に代入されてループが実行されます。elementは、自動的にcontainer内の要素の型に合わせて推論されます。要素の型がconstである場合、elementもconstになります。

利点[編集]

簡潔さ
イテレータを明示的に操作する必要がなくなるため、コードが簡潔になります。ループの目的が要素の処理であることが明確に表現されます。
読みやすさ
イテレータを使用するよりも自然な表現であり、コードの可読性が向上します。forループの本体が要素の処理に集中できます。
安全性
イテレータの有効性を気にする必要がなく、範囲外アクセスや無効なイテレータによる問題を回避できます。

実例[編集]

例えば、vector内の全ての要素を合計する場合を考えてみましょう。

#include <iostream>
#include <vector>

auto main() -> int {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    int sum = 0;
    
    for (auto num : numbers) {
        sum += num;
    }
    
    std::cout << "Sum: " << sum << std::endl;
    
    return 0;
}

このコードでは、範囲ベースのforループを使用して、numbers内の各要素を順番に取り出し、sumに加算しています。イテレータを使用するよりも簡潔で読みやすく、安全なコードとなっています。

範囲ベースのforループは、コンテナ内の要素を効率的に処理するための強力なツールです。コードをより簡潔にし、可読性を向上させるために活用しましょう。

カスタムコンテナ[編集]

C++では、ユーザーが独自のコンテナを定義することもできます。これは、特定のアプリケーションや要件に最適化されたデータ構造を実装する場合に有用です。カスタムコンテナの実装には、テンプレートやイテレータの活用が必要です。

カスタムコンテナの例として、簡単な動的配列の実装を考えてみましょう。このカスタムコンテナは、動的なサイズ変更と要素の追加・削除をサポートします。以下は、その一部を示したものです。

template<typename T>
class DynamicArray {
  private:
    T* array;
    int size;
    int capacity;
  public:
    // コンストラクタ、デストラクタ、メソッドの定義
};

コンテナの拡張とカスタマイズ[編集]

既存のコンテナを拡張したり、カスタマイズしたりすることは、特定の要件やアプリケーションに合わせてより効率的なデータ構造を作成するための重要な手法です。C++の柔軟な言語機能を活用することで、既存のコンテナを拡張することができます。また、STL(Standard Template Library)は、既存のコンテナを基に新しいコンテナを作成するための豊富な機能を提供しています。

既存のコンテナの拡張[編集]

既存のコンテナを拡張する方法の1つは、継承を利用することです。C++では、継承を使用して既存のクラスを拡張し、新しい機能を追加することができます。例えば、スタックやキューの機能を持つ新しいコンテナを作成する場合、既存のシーケンスコンテナ(例えば、vectorやlist)を基にして、新しいクラスを定義することができます。

以下は、既存のvectorを基にスタック機能を追加したStackクラスの例です。

#include <vector>

template <typename T> 
class Stack : public std::vector<T> {
  public:
    void push(const T &value) { this->push_back(value); }

    void pop() {
        if (!this->empty()) {
            this->pop_back();
        }
    }

    auto top() -> T & { return this->back(); }

    [[nodiscard]] auto empty() const -> bool { return this->size() == 0; }
};

このようにして、既存のvectorを継承して新しい機能を追加することができます。このStackクラスは、vectorの機能に加えて、スタック操作(push、pop、top)を提供します。

新しいコンテナの作成[編集]

STLは、既存のコンテナを基に新しいコンテナを作成するための便利な機能を提供しています。例えば、特定の要件に合わせて既存のコンテナを拡張するのではなく、新しいコンテナを完全に新規に作成することもできます。

以下は、新しいキューコンテナを作成する例です。

#include <list>

template <typename T> 
class Queue {
  private:
    std::list<T> elements;

  public:
    void push(const T &value) { elements.push_back(value); }

    void pop() {
        if (!empty()) {
            elements.pop_front();
        }
    }

    auto front() -> T & { return elements.front(); }

    [[nodiscard]] auto empty() const -> bool { return elements.empty(); }
};

このQueueクラスは、listを使用してキューを実装しています。これにより、先頭への要素の追加と削除がO(1)で行えます。

カスタムコンテナの利点[編集]

カスタムコンテナを作成することにより、特定の要件やアプリケーションに最適化されたデータ構造を実装することができます。また、既存のコンテナを拡張することで、より柔軟性の高いコードを作成することができます。しかし、カスタムコンテナを使用する際には、適切なテストとドキュメントが重要です。

実践的な例題[編集]

コンテナの理解を深めるために、いくつかの実践的な例題を解いてみましょう。以下の例題では、さまざまなシチュエーションでコンテナを使用する方法を示します。

整数の配列をソートするプログラム[編集]

この例題では、整数の配列をソートするプログラムを作成します。まず、ユーザーに整数の数を入力してもらい、その後、各整数を入力してもらいます。入力が完了したら、配列をソートし、ソートされた配列を出力します。

#include <iostream>
#include <vector>
#include <algorithm>

auto main() -> int {
    int n;
    std::cout << "Enter the number of integers: ";
    std::cin >> n;

    std::vector<int> numbers(n);

    std::cout << "Enter " << n << " integers:" << std::endl;
    for (int i = 0; i < n; i++) {
        std::cin >> numbers[i];
    }

    // 配列をソート
    std::sort(numbers.begin(), numbers.end());

    // ソートされた配列を出力
    std::cout << "Sorted array:" << std::endl;
    for (int i = 0; i < n; i++) {
        std::cout << numbers[i] << " ";
    }
    std::cout << std::endl;

    return 0;
}

このプログラムでは、vectorコンテナを使用して整数の配列を管理し、std::sortアルゴリズムを使用して配列をソートしています。

文字列のリストから特定の要素を検索するプログラム[編集]

この例題では、文字列のリストから特定の文字列を検索するプログラムを作成します。リストにはいくつかの文字列が含まれており、ユーザーが検索したい文字列を入力します。プログラムは、リストを走査し、検索文字列が見つかった場合にそのインデックスを表示します。

#include <iostream>
#include <list>
#include <string>

auto main() -> int {
    std::list<std::string> strings = {"apple", "banana", "orange", "grape", "melon"};

    std::string search;
    std::cout << "Enter a string to search: ";
    std::cin >> search;

    auto it = std::find(strings.begin(), strings.end(), search);
    if (it != strings.end()) {
        std::cout << "String found at index: " << std::distance(strings.begin(), it) << std::endl;
    } else {
        std::cout << "String not found." << std::endl;
    }

    return 0;
}

このプログラムでは、listコンテナを使用して文字列のリストを管理し、std::findアルゴリズムを使用して検索を行っています。見つかった場合はそのインデックスを表示し、見つからなかった場合はメッセージを表示します。

これらの例題を通じて、コンテナの基本的な操作やアルゴリズムの使用方法を実践的に学ぶことができます。

まとめと展望[編集]

この章では、C++のコンテナについて概説し、各種コンテナの特性や使用法を学びました。コンテナはプログラミングにおいて非常に重要な役割を果たし、適切に使用することで効率的なデータ管理と操作が可能となります。将来的には、C++の標準ライブラリやモダンなコンテナの進化にも注目し、より効率的なプログラミング技術を習得していきましょう。