C++ の最初の記事の 1 つで、ベクトル (理解と使用) をマスターできます。

ここに画像の説明を挿入

1. ベクトルとは何ですか?

C++ では、std::vector は標準テンプレート ライブラリ (STL) 内の動的配列コンテナーであり、任意の型の要素を格納でき、自動的にサイズ変更できます。std::vector には、配列の操作をより簡単かつ効率的に行う便利なメンバー関数が多数用意されています。
ここに画像の説明を挿入
ベクトル宣言:

template <class T, class Alloc = allocator<T> > ;
これは、std::vector の一般的なテンプレート定義です。これは 2 つのテンプレート パラメーター T と Alloc を使用します。ここで、T はコンテナーに格納されている要素のタイプを表し、Alloc はコンテナーのメモリ アロケーターのタイプを表し、デフォルトは std::allocator です。このテンプレートは、任意のタイプの要素を格納するための汎用ベクトル クラス テンプレートを定義します。

template <class Alloc> class vector<bool,Alloc>;
これは、bool 型の要素を格納する std::vector の特殊化です。これは、C++ の std::vector がブール型を直接格納できず、スペースを節約するために特別な実装を使用するためです。この特殊化は、コンテナーのメモリ アロケーター タイプを表すテンプレート パラメーター Alloc を受け入れますが、std::vector はスペースを節約するために特別な最適化を実行するため、このアロケーター タイプは実際には内部実装に影響しません。

要約:

template <class T, class Alloc = allocator<T> > ; std::vector の一般的なテンプレート定義であり、任意の型の要素を格納するために使用されます。
template <class Alloc> class vector<bool,Alloc>; bool 型の要素を格納するための std::vector の特殊なバージョンです。

機能と利点:

動的配列: std::vector は内部で動的メモリ管理を実装しており、必要に応じてストレージ容量を自動的に調整し、新しい要素を追加する必要がある場合には、より多くのメモリ領域を自動的に割り当てることができます。
ランダム アクセス: 要素には添字または反復子を介してアクセスでき、高速なランダム アクセスをサポートします。
自動拡張: 要素の数が現在の容量を超えると、 std::vector はより多くの要素を収容できるように、より大きなメモリ ブロックを自動的に再割り当てします。
連続ストレージ: std::vector の要素はメモリ内に連続して保存されるため、アクセス効率が向上します。
動的なサイズ変更のサポート: Push_back() メソッドと Pop_back() メソッドを使用して最後に要素を追加または削除できます。また、resize() メソッドを使用してコンテナのサイズを動的に変更することもできます。
他の動的シーケンス コンテナー (deque、list、forward_list) と比較すると、vector は要素にアクセスする際の効率が高く、最後に要素を追加および削除するのも比較的効率的です。最後ではない他の削除および挿入操作の場合、効率は低くなります。list および forward_list の統合イテレータと参照よりも優れています

<vector>std::vector を使用する前に、次のようなヘッダー ファイルをインクルードする必要があります#include <vector>

以下は std::vector を使用した簡単な例です

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> vec; // 声明一个存储int类型的vector

    // 在vector中添加元素
    vec.push_back(10);
    vec.push_back(20);
    vec.push_back(30);

    // 遍历vector并输出元素
    for (const auto& element : vec) {
    
    
        std::cout << element << " ";
    }
    std::cout << std::endl;

    return 0;
}

出力:

10 20 30

2. ベクターの使用

メンバータイプのリファレンス
ここに画像の説明を挿入

2.1 ベクトル コンストラクター

explicit vector (const allocator_type& alloc = allocator_type());	//构造一个空容器,不包含任何元素。
explicit vector (size_type n, const value_type& val = value_type(),
                 const allocator_type& alloc = allocator_type());//构造一个包含n 个元素的容器。每个元素都是val的副本。	
template <class InputIterator>
         vector (InputIterator first, InputIterator last,
                 const allocator_type& alloc = allocator_type());	//构造一个包含与范围[first,last)一样多的元素的容器,其中每个元素均从该范围内的相应元素按相同的顺序构造。
vector (const vector& x);//构造一个容器,其中包含x中每个元素的副本(按相同顺序)。

パラメータ:
alloc
アロケータ オブジェクト。
コンテナーは、このアロケーターの内部コピーを保持して使用します。
メンバー タイプ allocator_type は、コンテナーによって使用される内部アロケーター タイプで、2 番目のテンプレート パラメーター ( Alloc ) のエイリアスとしてベクター内で定義されます。
allocator_type がデフォルトのアロケーター (状態を持たない) のインスタンスである場合、これは無関係です。

n
初期コンテナ サイズ (つまり、構築時のコンテナ内の要素の数)。
メンバータイプ size_type は符号なし整数です。

val
コンテナに埋める値。コンテナ内の n 個の要素はそれぞれ、この値のコピーに初期化されます。
メンバー型 value_type は、コンテナー内の要素の型であり、最初のテンプレート パラメーター ( T ) のエイリアスとして Vector で定義されます。

first, last
範囲内の最初と最後の位置に反復子を入力します。使用される範囲は [first, last) で、first と last の間のすべての要素が含まれます。first が指す要素は含まれますが、last が指す要素は含まれません。
関数テンプレート パラメーター InputIterator は、value_type オブジェクトを構築できる型の要素を指す入力反復子型でなければなりません。

x
同じタイプ (同じクラス テンプレート パラメーター T および Alloc を持つ) の別の Vector オブジェクト。その内容はコピーまたは取得できます。

il
Initializer_list オブジェクト。
これらのオブジェクトは、初期化子リスト宣言子から自動的に構築されます。
メンバー型 value_type は、コンテナー内の要素の型であり、最初のテンプレート パラメーター ( T ) のエイリアスとして Vector で定義されます。

コンストラクターの分析と使用

explicit vector (const allocator_type& alloc = allocator_type());

これはデフォルトのコンストラクターです。空の std::vector オブジェクトを作成するデフォルトのアロケータ allocator_type を使用することも、パラメータ alloc を通じてカスタム アロケータを指定することもできます。

explicit vector (size_type n, const value_type& val = value_type(), const allocator_type& alloc = allocator_type());

このコンストラクターは次の目的で使用されます。n 個の要素を含む std::vector オブジェクトを作成します。すべて val で初期化されます。n は初期化される要素の数を表し、val は初期化される要素の値を表し、alloc は使用されるアロケータを表します。

template <class InputIterator> vector (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type());

これはイテレータ範囲から std::vector オブジェクトを作成できるテンプレート コンストラクター。それぞれイテレータの開始位置と終了位置を表す 2 つのパラメータ first と last を受け入れます。コンストラクターでは、 std::vector オブジェクトが最初と最後の反復子が指す要素の範囲で初期化され、 alloc は使用されるアロケーターを示します。

vector (const vector& x);

これは、パラメーター x と同じ型および内容の新しい std::vector オブジェクトを作成するために使用されるコピー コンストラクターです。新しい std::vector オブジェクトを作成すると、x の要素が新しいオブジェクトにコピーされますが、新しいオブジェクトは元のオブジェクトから独立しており、一方のオブジェクトを変更してももう一方のオブジェクトには影響しません。

例:

#include <iostream>
#include <vector>

int main ()
{
    
    
  //构造函数的使用顺序与上述相同:
  std::vector<int> first;                                // 默认构造
  std::vector<int> second (4,100);                       // 初始化四个值为 100 的整数
  std::vector<int> third (second.begin(),second.end());  // 使用迭代器的拷贝
  std::vector<int> fourth (third);                       // 拷贝构造

  // 迭代器构造函数也可用于从数组构造:
  int myints[] = {
    
    16,2,77,29};
  std::vector<int> fifth (myints, myints + sizeof(myints) / sizeof(int) );
  
  return 0;
}

またここで代入演算子のオーバーロード
vector& operator= (const vector& x);
クラスのコピー代入演算子のオーバーロード関数。既存の std::vector オブジェクト x の内容を別の既存の std::vector オブジェクトにコピーし、割り当てられたオブジェクトの参照を返すために使用されます。

この演算子オーバーロードは 2 つの std::vector オブジェクトの内容を同じにしますが、これらは独立しており、一方のオブジェクトを変更しても他方のオブジェクトには影響しません。

例:

std::vector<int> vec1 = {
    
    1, 2, 3, 4};
std::vector<int> vec2 = {
    
    5, 6, 7};

vec1 = vec2; // 将 vec2 的内容赋值给 vec1

上の例では、vec1 の内容は vec2 と同じ {5, 6, 7} になります。vec2 自体は影響を受けません。

2.2 ベクトル反復子(Iterators)関数

2.2.1 begin()

iterator begin();
const_iterator begin() const;

iterator begin();および は const_iterator begin() const;std::vector クラスのメンバー関数であり、次の目的で使用されます。変更可能な要素のイテレータと定数要素のイテレータを取得します

iterator begin();: std::vector コンテナへのポインタを返します。最初の要素に対する変更可能な反復子このイテレータを通じて、コンテナ内の要素を変更できます。
const_iterator begin() const;: std::vector コンテナへのポインタを返します。最初の要素への const イテレータこのイテレータを通じて、コンテナ内の要素を横断することができますが、コンテナ内の要素を変更することはできません。

使用例:

#include <vector>
#include <iostream>

int main() {
    
    
    std::vector<int> myVector = {
    
    1, 2, 3, 4, 5};

    // 使用 begin() 获取可修改迭代器
    std::vector<int>::iterator it = myVector.begin();

    // 使用 begin() 获取常量迭代器
    std::vector<int>::const_iterator cit = myVector.begin();

    // 修改第一个元素的值
    *it = 10;

    // 错误:常量迭代器不允许修改元素的值
    // *cit = 20;

    // 输出容器中的元素
    for (int num : myVector) {
    
    
        std::cout << num << " ";
    }

    return 0;
}

上記の例では、整数を格納する std::vector オブジェクト myVector を作成し、begin() を使用して変更可能な反復子 it と定数反復子 cit を取得します。変更可能な反復子 it を使用すると、コンテナ内の要素の値を変更できますが、定数反復子 cit を使用して、コンテナ内の要素の値を変更することはできません。最後に、コンテナ内の要素を反復して出力します。出力は「10 2 3 4 5」になります。

2.2.2 end()

iterator end();
const_iterator end() const;

iterator end(); および はconst_iterator end() const;std::vector クラスのメンバー関数です。コンテナ内の最後の要素の次の位置を指す、変更可能な要素のイテレータと定数要素のイテレータを取得します。

iterator end();: std::vector コンテナへのポインタを返します。最後の要素の次の位置への変更可能な反復子このイテレータを通じて、コンテナ内の要素を変更できます。
const_iterator end() const;: std::vector コンテナへのポインタを返します。最後の要素の次の位置への const イテレータこのイテレータを通じて、コンテナ内の要素を横断することができますが、コンテナ内の要素を変更することはできません。

使用例:

#include <vector>
#include <iostream>

int main() {
    
    
    std::vector<int> myVector = {
    
    1, 2, 3, 4, 5};

    // 使用 end() 获取可修改迭代器
    std::vector<int>::iterator it = myVector.end();

    // 使用 end() 获取常量迭代器
    std::vector<int>::const_iterator cit = myVector.end();

    // 修改最后一个元素的值
    *(--it) = 100;

    // 错误:常量迭代器不允许修改元素的值
    // *(--cit) = 200;

    // 输出容器中的元素
    for (int num : myVector) {
    
    
        std::cout << num << " ";
    }

    return 0;
}

上記の例では、整数を格納する std::vector オブジェクト myVector を作成し、end() を使用して変更可能な反復子 it と定数反復子 cit を取得します。変更可能な反復子 it を使用すると、コンテナ内の要素の値を変更できますが、定数反復子 cit を使用して、コンテナ内の要素の値を変更することはできません。最後に、コンテナ内の要素を走査して出力します。出力は「1 2 3 4 100」になります。最後の要素の値を変更するときは、 --it を使用してイテレータを最後の要素の位置に移動したことに注意してください。end() によって返されるイテレータはコンテナ内の最後の要素の次の位置を指すため、最後の要素を変更する場合は、最初にイテレータを最後の要素の位置に移動する必要があります。

2.2.3 rbegin()

reverse_iterator rbegin();
const_reverse_iterator rbegin() const;

reverse_iterator rbegin();と は、const_reverse_iterator rbegin() const;逆反復子を取得するための std::vector クラスのメンバー関数です。

reverse_iterator rbegin();: std::vector コンテナへのポインタを返します。最後の要素の変更可能な逆反復子逆反復子は、コンテナの最後から要素を前方に走査する特別な反復子です。
const_reverse_iterator rbegin() const;: std::vector コンテナへのポインタを返します。const 最後の要素への逆反復子この逆反復子を介して、要素をコンテナの末尾から前方にトラバースすることができますが、コンテナ内の要素を変更することはできません。

使用例:

#include <vector>
#include <iostream>

int main() {
    
    
    std::vector<int> myVector = {
    
    1, 2, 3, 4, 5};

    // 使用 rbegin() 获取可修改逆向迭代器
    std::vector<int>::reverse_iterator rit = myVector.rbegin();

    // 使用 rbegin() 获取常量逆向迭代器
    std::vector<int>::const_reverse_iterator crit = myVector.rbegin();

    // 修改最后一个元素的值
    *rit = 100;

    // 错误:常量逆向迭代器不允许修改元素的值
    // *crit = 200;

    // 输出容器中的元素
    for (int num : myVector) {
    
    
        std::cout << num << " ";
    }

    return 0;
}

上記の例では、整数を格納する std::vector オブジェクト myVector を作成し、rbegin() を使用して、変更可能な逆反復子 rit と定数逆反復子 crit を取得します。変更可能な逆反復子 rit を通じて、コンテナー内の要素の値を変更できますが、定数逆反復子 crit を通じて、コンテナー内の要素の値を変更することはできません。最後に、コンテナ内の要素を反復して出力します。出力は「100 4 3 2 1」になります。逆反復子が指す位置が最後の要素であるため、最後の要素の値を変更するときは *rit を介して直接変更することに注意してください。

2.2.4 レンド()

reverse_iterator rend();
const_reverse_iterator rend() const;

reverse_iterator rend();および はconst_reverse_iterator rend() const;std::vector クラスのメンバー関数であり、逆反復子の終了位置を取得するために使用されます。

reverse_iterator rend();: std::vector コンテナへのポインタを返します。逆反復子の終了位置に対する変更可能な逆反復子逆反復子の終了位置は、実際にはコンテナ内の最初の要素の前の位置です。
const_reverse_iterator rend() const;: std::vector コンテナへのポインタを返します。const 逆反復子を逆反復子の終了位置までこの逆反復子を使用すると、コンテナーの最初の要素の前の位置から要素を逆方向にトラバースできますが、コンテナー内の要素を変更することはできません。

使用例:

#include <vector>
#include <iostream>

int main() {
    
    
    std::vector<int> myVector = {
    
     1, 2, 3, 4, 5 };

    // 使用 rend() 获取可修改逆向迭代器的结束位置
    std::vector<int>::reverse_iterator ritEnd = myVector.rend();

    // 使用 rend() 获取常量逆向迭代器的结束位置
    std::vector<int>::const_reverse_iterator critEnd = myVector.rend();

    // 遍历输出容器中的元素
    std::cout << "Using const_reverse_iterator: ";
    for (std::vector<int>::const_reverse_iterator crit = myVector.rbegin(); crit != critEnd; ++crit) {
    
    
        //*rit += 1; //错误:常量逆向迭代器不允许修改元素的值
        std::cout << *crit << " ";
    }
    std::cout << std::endl;

    // 遍历输出容器中的元素
    std::cout << "Using reverse_iterator: ";
    for (std::vector<int>::reverse_iterator rit = myVector.rbegin(); rit != ritEnd; ++rit) {
    
    
        *rit += 1;// 从后往前修改每一个元素的值
        std::cout << *rit << " ";
    }
    std::cout << std::endl;

    return 0;
}

上記の例では、 rend() を使用して、変更可能な逆反復子の終了位置 ritEnd と定数逆反復子の終了位置 critEnd をそれぞれ取得します。次に、2 つの終了位置の逆反復子をそれぞれ使用して、出力コンテナー内の要素を反復処理します。逆反復子を使用してコンテナーを走査する場合、コンテナーの最初の要素の前の位置への範囲外アクセスを避けるために、終了条件を rit != ritEnd または crit != critEnd に設定する必要があることに注意してください。出力は次のようになります。

Using const_reverse_iterator: 5 4 3 2 1
Using reverse_iterator: 6 5 4 3 2

2.2.5 cbegin()、cend()、crbegin()およびcrend() C++11

C++11 標準では、 std::vector クラス テンプレートに、定数反復子を取得するための次のメンバー関数が導入されています。

cbegin(): std::vector コンテナへのポインタを返します最初の要素への const イテレータ。
cend(): std::vector コンテナへのポインタを返します最後の要素の後の位置への定数反復子。
crbegin(): std::vector コンテナへのポインタを返します最後の要素への const 逆反復子。
crend(): std::vector コンテナへのポインタを返します最初の要素の前の位置への const 逆反復子。

std::vector でのこれらの関数の使用例は次のとおりです。

#include <vector>
#include <iostream>

int main() {
    
    
    std::vector<int> myVector = {
    
    1, 2, 3, 4, 5};

    // 使用 cbegin() 获取常量迭代器的开始位置
    std::vector<int>::const_iterator citBegin = myVector.cbegin();

    // 使用 cend() 获取常量迭代器的结束位置
    std::vector<int>::const_iterator citEnd = myVector.cend();

    // 使用 crbegin() 获取常量逆向迭代器的开始位置
    std::vector<int>::const_reverse_iterator critRBegin = myVector.crbegin();

    // 使用 crend() 获取常量逆向迭代器的结束位置
    std::vector<int>::const_reverse_iterator critREnd = myVector.crend();

    // 遍历输出容器中的元素
    std::cout << "Using const_iterator: ";
    for (std::vector<int>::const_iterator cit = citBegin; cit != citEnd; ++cit) {
    
    
        std::cout << *cit << " ";
    }
    std::cout << std::endl;

    // 遍历输出容器中的元素
    std::cout << "Using const_reverse_iterator: ";
    for (std::vector<int>::const_reverse_iterator crit = critRBegin; crit != critREnd; ++crit) {
    
    
        std::cout << *crit << " ";
    }
    std::cout << std::endl;

    return 0;
}

上記の例では、 cbegin() と cend() を使用して定数反復子の開始位置と終了位置を取得し、 crbegin() と crend() を使用して定数逆反復子の開始位置と終了位置を取得します。次に、これらの定数反復子をそれぞれ使用して、出力コンテナー内の要素を反復処理します。出力は次のようになります。

Using const_iterator: 1 2 3 4 5 
Using const_reverse_iterator: 5 4 3 2 1 

定数イテレータは、コンテナ内の要素はコンテナを走査している間は変更できず、読み取りのみであることを意味することに注意してください。

2.3 ベクトル容量関数

C++ の std::vector クラス テンプレートでは、コンテナーのサイズと容量を管理するために次のメンバー関数が使用されます。

size_type size() const;: 現在の std::vector コンテナに戻ります実際に格納される要素の数
size_type max_size() const;: std::vector コンテナを返す含めることができる要素の最大数、この値はコンパイラとシステムの制限によって異なります。
void resize (size_type n, value_type val = value_type());: 変化std::vector コンテナー要素の数、できる要素の数を増減する
size_type capacity() const;: 現在の std::vector コンテナを返します。メモリを再割り当てせずに保存できる要素の最大数
bool empty() const;: std::vector コンテナが空かどうかを確認し、空の場合は空の場合は true を返し、それ以外の場合は false を返します
void reserve (size_type n);: std::vector コンテナーをリクエストします少なくとも指定された数の要素用にメモリ領域を予約しますが、実際に要素を作成するわけではありません。
void shrink_to_fit();: std::vector コンテナが必要です現在保存されている要素の数内に収まるようにメモリ使用量を削減します。、ただし、必要なサイズに縮小するという保証はありません。(C++11)

各機能の簡単な紹介は次のとおりです。

size(): 現在の std::vector コンテナに実際に格納されている要素の数を返します。n 要素を持つ std::vector の場合、size() は n を返します。
max_size(): std::vector コンテナに含めることができる要素の最大数を返します。この値はコンパイラとシステムの制限によって異なります。通常、この値は非常に大きく、実際に必要な要素の数をはるかに超えます。
resize(): std::vector コンテナ内の要素の数を変更します。新しいサイズが現在のサイズより小さい場合、冗長な要素は削除されます。新しいサイズが現在のサイズより大きい場合、新しい要素にはデフォルト値または指定された値が入力されます。
capacity(): 現在の std::vector コンテナーがメモリを再割り当てせずに格納できる要素の最大数を返します。Capacity() は、std::vector 内の要素の数を変更しません。
empty(): std::vector コンテナが空かどうかを確認し、空の場合は true を返し、そうでない場合は false を返します。空のコンテナーは、size() が値 0 を返すことを意味します。
reserve(): std::vector コンテナに対して、少なくとも指定された数の要素用のメモリ領域を予約するように要求しますが、実際に要素を作成するわけではありません。この関数は通常、頻繁なメモリの割り当てと割り当て解除の操作を回避するために使用されます。
shrink_to_fit(): 現在格納されている要素の数に合わせてメモリ使用量を縮小するように std::vector コンテナに要求しますが、必要なサイズに縮小することは保証されません。この関数は通常、多数の要素を削除して不要になったメモリを解放した後に呼び出されます。

std::vector コンテナーでのこれらの関数の使用例は次のとおりです。

#include <vector>
#include <iostream>

int main() {
    
    
    std::vector<int> myVector = {
    
    1, 2, 3, 4, 5};

    std::cout << "Size: " << myVector.size() << std::endl;
    std::cout << "Max Size: " << myVector.max_size() << std::endl;

    myVector.resize(8, 10); // 增加元素并用值 10 填充
    std::cout << "Resized Size: " << myVector.size() << std::endl;
    std::cout << "Capacity: " << myVector.capacity() << std::endl;

    std::cout << "Is Empty: " << (myVector.empty() ? "Yes" : "No") << std::endl;

    myVector.reserve(10); // 预留内存,但不实际创建元素
    std::cout << "New Capacity After Reserve: " << myVector.capacity() << std::endl;

    myVector.shrink_to_fit(); // 尝试缩小内存使用
    std::cout << "New Capacity After Shrink: " << myVector.capacity() << std::endl;

    return 0;
}

出力は次のようになります。

Size: 5
Max Size: 4611686018427387903
Resized Size: 8
Capacity: 8
Is Empty: No
New Capacity After Reserve: 8
New Capacity After Shrink: 8

:capacity() と size() の違いは、capacity() がメモリを再割り当てせずに std::vector が保持できる要素の最大数を表すのに対し、size() は std::vector 内の実際のストレージの数を表すことです。要素。

こうなってくると、ベクターの展開機構についても触れておかなければなりません。

容量拡張を使用して vs および g++ でそれぞれ実行すると、vs での容量は 1.5 倍、g++ では 2 倍に増加することがわかります。この質問はよく調査されますが、ベクターの容量が 2 倍になるとは考えないでください。具体的な増加量は特定のニーズに応じて定義されます。vs は PJ バージョン STL、g++ は SGI バージョン STL です。

コードは以下のように表示されます:

#include <iostream>
#include <vector>
using namespace std;

void TestVectorExpand()
{
    
    
    size_t sz;
    vector<int> v;
    sz = v.capacity();
    cout << "making v grow:\n";
    for (int i = 0; i < 100; ++i)
    {
    
    
        v.push_back(i);
        if (sz != v.capacity())
        {
    
    
            sz = v.capacity();
            cout << "capacity changed: " << sz << '\n';
        }
    }
}
int main() {
    
    

    TestVectorExpand();
    return 0;
}

VS2022 の実行結果
ここに画像の説明を挿入
g++ の実行結果
ここに画像の説明を挿入
このような問題に直面した場合は、reserve を使用してベクトル展開のコストの問題を軽減するか、resize を使用します。これはスペースを開くときに初期化され、サイズに影響します。


ベクトルに格納するおおよその要素数がすでに決まっている場合は、挿入や拡張によって発生する効率の低下の問題を回避するために、事前に十分なスペースを設定できます。

#include <iostream>
#include <vector>
using namespace std;

void TestVectorExpandOP()
{
    
    
    vector<int> v;
    size_t sz = v.capacity();
    v.reserve(100); // 提前将容量设置好,可以避免一遍插入一遍扩容
    cout << "making bar grow:\n";
    for (int i = 0; i < 100; ++i)
    {
    
    
        v.push_back(i);
        if (sz != v.capacity())
        {
    
    
            sz = v.capacity();
            cout << "capacity changed: " << sz << '\n';
        }
    }
}
int main() {
    
    

    TestVectorExpandOP();
    return 0;
}

ここに画像の説明を挿入

2.4 ベクトル要素アクセス関数

2.4.1 演算子[]

reference operator[] (size_type n);
const_reference operator[] (size_type n) const;

std::vector は、インデックスによってコンテナ内の要素にアクセスするための 2 つのオーバーロードされた Operator[] を提供します。

reference operator[] (size_type n): このオーバーロードは std::vector にアクセスするために使用されますインデックス位置 n の要素を指定し、その要素への参照を返します。この過負荷により、要素の読み取りと変更が可能もしもn が有効なインデックス範囲外 (つまり、0 未満、または size() 以上) の場合、動作は未定義です(未定義の動作)。
const_reference operator[] (size_type n) const;: このオーバーロードは、const で装飾された std::vector オブジェクト内の指定されたインデックス位置 n にある要素にアクセスし、要素への定数参照を返すために使用される const メンバー関数です。この過負荷により、要素は読み取りのみ可能であり、要素の値は変更できません

以下は、operator[] を使用した例です。

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> myVector = {
    
    1, 2, 3, 4, 5};

    // 修改元素
    myVector[2] = 10; // 将索引 2 处的元素修改为 10

    // 读取元素
    std::cout << "Element at index 2: " << myVector[2] << std::endl;

    // 使用 const_reference 进行读取(适用于 const std::vector)
    const std::vector<int>& constVector = myVector;
    std::cout << "Const element at index 4: " << constVector[4] << std::endl;

    return 0;
}

出力結果:

Element at index 2: 10
Const element at index 4: 5

Operator[] を通じて要素にアクセスする場合は、インデックスが有効な範囲内にあることを確認する必要があります。そうしないと、未定義の動作が発生する可能性があります。通常、要素にアクセスする前に、size() 関数を使用してインデックスの有効性をチェックする必要があります。

2.4.2 で

reference at (size_type n);
const_reference at (size_type n) const;

std::vector は、インデックスによってコンテナ内の要素にアクセスするための 2 つのオーバーロードされた at 関数を提供します。

reference at (size_type n): このオーバーロードは std::vector にアクセスするために使用されますインデックス位置 n の要素を指定し、その要素への参照を返します。このオーバーロードを使用すると、要素の読み取りと変更が可能になりますnの場合有効なインデックス範囲外です(つまり、0 未満、または size() 以上)、at 関数は std::out_of_range 例外をスローします、プログラムの堅牢性を確保するため。
const_reference at (size_type n) const;: このオーバーロードは、const で装飾された std::vector オブジェクト内の指定されたインデックス位置 n にある要素にアクセスし、要素への定数参照を返すために使用される const メンバー関数です。この過負荷により、要素は読み取りのみ可能であり、要素の値は変更できません

at 関数を使用した例を次に示します。

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> myVector = {
    
    1, 2, 3, 4, 5};

    // 修改元素
    myVector.at(2) = 10; // 将索引 2 处的元素修改为 10

    // 读取元素
    std::cout << "Element at index 3: " << myVector.at(3) << std::endl;

    // 使用 const_reference 进行读取(适用于 const std::vector)
    const std::vector<int>& constVector = myVector;
    std::cout << "Const element at index 4: " << constVector.at(4) << std::endl;

    // 尝试访问越界的索引(会抛出 std::out_of_range 异常)
    try {
    
    
        int value = myVector.at(10);
    } catch (const std::out_of_range& e) {
    
    
        std::cout << "Caught exception: " << e.what() << std::endl;
    }

    return 0;
}

出力結果:

Element at index 3: 4
Const element at index 4: 5
Caught exception: vector::_M_range_check: __n (which is 10) >= this->size() (which is 5)

範囲外のインデックスにアクセスしようとすると、at 関数が std::out_of_range 例外をスローし、アクセス インデックスが範囲外であることを通知していることがわかります。したがって、at 関数を使用すると、特にインデックスが有効かどうか不明な場合に、operator[] よりも要素に安全にアクセスできます。

2.4.3 フロント()

reference front();
const_reference front() const;

std::vector は、コンテナー内の最初の要素にアクセスするための 2 つのオーバーロードされたフロント関数を提供します。

reference front(): このオーバーロードは、std::vector コンテナーにアクセスするために使用されます。最初の要素を取得し、その要素への参照を返しますこのオーバーロードを使用すると、要素に対して読み取りおよび変更操作を実行できます。
const_reference front() const;: このオーバーロードは、const で装飾された std::vector オブジェクトの最初の要素にアクセスし、その要素への定数参照を返す const メンバー関数です。この過負荷により、要素は読み取りのみ可能であり、要素の値は変更できません

フロント関数を使用した例を次に示します。

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> myVector = {
    
    1, 2, 3, 4, 5};

    // 修改第一个元素
    myVector.front() = 10; // 将第一个元素修改为 10

    // 读取第一个元素
    std::cout << "First element: " << myVector.front() << std::endl;

    // 使用 const_reference 进行读取(适用于 const std::vector)
    const std::vector<int>& constVector = myVector;
    std::cout << "Const first element: " << constVector.front() << std::endl;

    return 0;
}

出力結果:

First element: 10
Const first element: 10

ご覧のとおり、front 関数は std::vector コンテナーの最初の要素を取得するために使用され、さまざまなオーバーロードされたバージョンを使用して、通常オブジェクトと const オブジェクトの両方に適した読み取りおよび変更操作を実装できます。

2.4.4 戻る()

reference back();
const_reference back() const;

std::vector は、コンテナ内の最後の要素にアクセスするための 2 つのオーバーロードされた back 関数を提供します。

reference back(): このオーバーロードは、std::vector コンテナーにアクセスするために使用されます。最後の要素を取得し、その要素への参照を返しますこのオーバーロードを使用すると、要素に対して読み取りおよび変更操作を実行できます。
const_reference back() const;: このオーバーロードは、const で装飾された std::vector オブジェクトの最後の要素にアクセスするために使用される const メンバー関数であり、その要素への定数参照を返します。この過負荷により、要素は読み取りのみ可能であり、要素の値は変更できません

以下は back 関数を使用した例です。

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> myVector = {
    
    1, 2, 3, 4, 5};

    // 修改最后一个元素
    myVector.back() = 10; // 将最后一个元素修改为 10

    // 读取最后一个元素
    std::cout << "Last element: " << myVector.back() << std::endl;

    // 使用 const_reference 进行读取(适用于 const std::vector)
    const std::vector<int>& constVector = myVector;
    std::cout << "Const last element: " << constVector.back() << std::endl;

    return 0;
}

出力結果:

Last element: 10
Const last element: 10

ご覧のとおり、 back 関数は std::vector コンテナー内の最後の要素を取得するために使用され、さまざまなオーバーロードされたバージョンを使用して、通常オブジェクトと const オブジェクトの両方に適した読み取りおよび変更操作を実装できます。

2.4.5 data() C++11

value_type* data() noexcept;
const value_type* data() const noexcept;

std::vector は、基になるストレージ配列へのポインターにアクセスするための 2 つのオーバーロードされたデータ関数を提供します。

value_type* data() noexcept;: このオーバーロードは std::vector コンテナへのポインタを返します。基礎となるストレージ配列への非定数ポインタこのオーバーロードにより、基になる配列に対する読み取りおよび書き込み操作が可能になります。noexc は、この関数が例外をスローしないことを意味します
const value_type* data() const noexcept;: このオーバーロードは、std::vector コンテナへのポインタを返す const メンバー関数です。基礎となるストレージアレイへの定数ポインタこのオーバーロードを使用すると、基になる配列のみを読み取ることができ、配列の内容を変更することはできません。noexc は、この関数が例外をスローしないことを意味します。

データ関数を使用した例を次に示します。

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> myVector = {
    
    1, 2, 3, 4, 5};

    // 获取底层存储数组的非常量指针
    int* ptr = myVector.data();

    // 修改底层存储数组
    ptr[0] = 10;
    ptr[3] = 20;

    // 通过 const value_type* 获取底层存储数组的常量指针(适用于 const std::vector)
    const std::vector<int>& constVector = myVector;
    const int* constPtr = constVector.data();

    // 读取底层存储数组
    for (size_t i = 0; i < myVector.size(); ++i) {
    
    
        std::cout << "Element " << i << ": " << constPtr[i] << std::endl;
    }

    return 0;
}

出力結果:

Element 0: 10
Element 1: 2
Element 2: 3
Element 3: 20
Element 4: 5

data 関数は、std::vector コンテナーの基になるストレージ配列へのポインターを取得するために使用され、通常のオブジェクトと const オブジェクトに適したさまざまなオーバーロードされたバージョンを通じて読み取りおよび変更操作を実装できることがわかります。data 関数を使用する場合、std::vector が空でないことを確認する必要があることに注意してください。空でない場合は、取得されたポインターが null ポインターになる可能性があります。

2.5 ベクトルの追加、削除、チェック、変更機能

2.5.1 assign()

template <class InputIterator> void assign (InputIterator first, InputIterator last);
void assign (size_type n, const value_type& val);

std::vector は、std::vector の内容を置き換えるための 2 つのオーバーロードされた割り当て関数を提供します。

template <class InputIterator> void assign (InputIterator first, InputIterator last);:

  1. このオーバーロードされたテンプレート関数は、次のペアを受け入れます。最初と最後の反復子は、範囲を表すパラメーターとして取得されます。assign 関数はコンテナーのコンテンツは指定された範囲の要素に置き換えられます
  2. このオーバーロードされた関数あらゆる種類のイテレータを受け入れることができます、ポインタ、通常のイテレータ、const イテレータなど。指定された範囲が有効である限り、assign 関数はコンテナの内容をその範囲内の要素に置き換えます。

void assign (size_type n, const value_type& val);:

  1. このオーバーロードされた関数は、引数として整数 n と値 val を受け取ります。コンテナの内容を n val 値に置き換えます。
  2. n は、新しいコンテナーに含める必要がある val 値の数を示します。コンテナのサイズは n に設定され、元の要素が置き換えられます。

assign 関数を使用した例を次に示します。

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> myVector;

    // 使用 assign 重载函数替换容器内容为一组元素
    std::vector<int> sourceVector = {
    
    10, 20, 30, 40, 50};
    myVector.assign(sourceVector.begin(), sourceVector.end());

    // 打印容器内容
    for (int num : myVector) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    // 使用 assign 重载函数替换容器内容为多个相同的元素
    myVector.assign(5, 100);

    // 打印容器内容
    for (int num : myVector) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

出力結果:

10 20 30 40 50 
100 100 100 100 100 

この例では、最初に assign 関数を使用して、myVector コンテナの内容を別のベクトル、sourceVector の要素に置き換えます。次に、再度 assign 関数を使用して、myVector の内容を値 100 の 5 つの要素に置き換えます。

2.5.2 Push_back()

void push_back (const value_type& val);

std::vector の Push_back 関数は、コンテナーの末尾に新しい要素を追加するために使用されます。追加される新しい要素の値を表す参照パラメーター const value_type& val を受け取ります。

ここで、value_type は std::vector 内の要素のタイプであり、通常はテンプレート パラメーターで指定されたタイプです。const は、渡された値が関数内で変更されないことを意味します。

以下は、push_back 関数を使用した例です。

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> myVector;

    myVector.push_back(10);
    myVector.push_back(20);
    myVector.push_back(30);

    // 打印容器内容
    for (int num : myVector) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

出力結果:

10 20 30

2.5.3 ポップバック()

void pop_back();

std::vector の Pop_back 関数は、コンテナの末尾から要素を削除するために使用されます。引数は取らず、単にコンテナの末尾から最後の要素を削除します。

以下は、pop_back 関数を使用した例です。

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> myVector = {
    
    10, 20, 30};

    std::cout << "Before pop_back: ";
    for (int num : myVector) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    myVector.pop_back();

    std::cout << "After pop_back: ";
    for (int num : myVector) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

出力結果:

Before pop_back: 10 20 30 
After pop_back: 10 20

この例では、std::vector コンテナーを作成し、3 つの要素 10、20、および 30 を初期化します。次に、pop_back 関数を使用して、コンテナから最後の要素 30 を削除します。

2.5.4 挿入()

iterator insert (iterator position, const value_type& val);
void insert (iterator position, size_type n, const value_type& val);
template <class InputIterator> void insert (iterator position, InputIterator first, InputIterator last);

std::vector の挿入関数は、指定された位置に 1 つ以上の要素を挿入するために使用されます。

1. 単一要素の挿入:
このバージョンの挿入関数は、位置の前に要素を挿入し、挿入された要素を指す反復子を返します。

例:

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> myVector = {
    
    1, 2, 3};

    std::vector<int>::iterator it = myVector.begin() + 1;
    myVector.insert(it, 100);

    for (int num : myVector) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

出力結果:

1 100 2 3 

2. 複数の同一の要素を挿入します。
このバージョンの挿入関数は、位置position の前に値 val を持つ n 個の要素を挿入します。

例:

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> myVector = {
    
    1, 2, 3};

    std::vector<int>::iterator it = myVector.begin() + 1;
    myVector.insert(it, 3, 100);

    for (int num : myVector) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

出力結果:

1 100 100 100 2 3 

3. 範囲内に要素を挿入する:
このバージョンの挿入関数は、位置position の前の最初から最後までの範囲内の要素を挿入します。

例:

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> myVector = {
    
    1, 2, 3};
    std::vector<int> toInsert = {
    
    100, 200};

    std::vector<int>::iterator it = myVector.begin() + 1;
    myVector.insert(it, toInsert.begin(), toInsert.end());

    for (int num : myVector) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

出力結果:

1 100 200 2 3 

すべての挿入関数では、パラメーターの位置は、挿入される位置を指定する反復子です。これらの関数は、コンテナー内の位置を決定する前に要素を挿入し、他の要素を調整します。

2.5.5 消去()

iterator erase (iterator position);
iterator erase (iterator first, iterator last);

std::vector の消去関数は、コンテナーから要素または要素の範囲を削除するために使用されます。

1. 単一の要素を削除する:
このバージョンの消去関数は、position が指す要素を削除し、次の要素を指す反復子を返します。

例:

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> myVector = {
    
    1, 2, 3, 4, 5};

    std::vector<int>::iterator it = myVector.begin() + 2;
    myVector.erase(it);

    for (int num : myVector) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

出力結果:

1 2 4 5 

2. 要素の範囲を削除する
このバージョンの消去関数は、最初から最後までの範囲内の要素を削除し、次の要素を指す反復子を返します。

例:

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> myVector = {
    
    1, 2, 3, 4, 5};

    std::vector<int>::iterator it1 = myVector.begin() + 1;
    std::vector<int>::iterator it2 = myVector.begin() + 3;
    myVector.erase(it1, it2);

    for (int num : myVector) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

出力結果:

1 4 5 

すべての消去関数では、引数は、削除する要素の位置を指定する範囲を表す反復子または反復子のペアにすることができます。これらの関数は、指定された要素を削除し、コンテナ内の他の要素を調整します。削除後、イテレータは無効になるため、注意して使用する必要があります。

2.5.6 スワップ()

void swap (vector& x);

std::vector の swap 関数は、2 つの連続したリストの内容を交換するために使用されます。現在のシーケンス テーブルとパラメータ シーケンス テーブル x の内容を交換します。

例:

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> vec1 = {
    
    1, 2, 3};
    std::vector<int> vec2 = {
    
    4, 5, 6};

    std::cout << "Before swap:" << std::endl;
    for (int num : vec1) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    for (int num : vec2) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    vec1.swap(vec2);

    std::cout << "After swap:" << std::endl;
    for (int num : vec1) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    for (int num : vec2) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

出力結果:

Before swap:
1 2 3 
4 5 6 
After swap:
4 5 6 
1 2 3 

:スワップ機能はコンテナの容量ではなくコンテナの内容を交換するため、交換後も 2 つのコンテナの容量は同じままになります。これにより、スワップ後にコンテナーの容量が実際の要素よりも大きくなり、余分なメモリ割り当てと無駄が発生する可能性があります。2 つのコンテナの容量を交換したい場合は、std::vector の移動コンストラクタと移動代入演算子を使用できます。

2.5.7 クリア()

void clear();

clear 関数は、std::vector 内のすべての要素をクリアして、空のシーケンス リストにするために使用されます。

例:

#include <iostream>
#include <vector>

int main() {
    
    
    std::vector<int> vec = {
    
    1, 2, 3, 4, 5};

    std::cout << "Before clear:" << std::endl;
    for (int num : vec) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    vec.clear();

    std::cout << "After clear:" << std::endl;
    for (int num : vec) {
    
    
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

出力結果:

Before clear:
1 2 3 4 5 
After clear:

:Clear 関数はコンテナ内の要素をクリアするだけであり、コンテナの容量は変更しません。要素のクリアとメモリの解放を同時に行いたい場合は、std::vector の shrin_to_fit 関数を使用できます。

2.5.8 place() C++11

template <class... Args> iterator emplace (const_iterator position, Args&&... args);

emplace 関数は std::vector のメンバー関数であり、指定された位置に新しい要素を挿入し、パラメーターを渡すことによって要素を構築するために使用されます。追加のコピーや移動操作を行わずに、コンテナ内の指定された位置での要素のインプレース構築をサポートします。

パラメータの説明:

Position:挿入位置を指定する反復子。新しい要素がこの位置の前に挿入されることを示します。
args:新しい要素の構築パラメーターを表す可変個引数テンプレート。

戻り値:

挿入された新しい要素を指す反復子を返します。

例:

#include <iostream>
#include <vector>

class Person {
    
    
public:
    Person(const std::string& name, int age) : name_(name), age_(age) {
    
    
        std::cout << "Constructing " << name_ << ", Age: " << age_ << std::endl;
    }

private:
    std::string name_;
    int age_;
};

int main() {
    
    
    std::vector<Person> people;
    
    // 使用 emplace 在位置 0 处插入新元素
    people.emplace(people.begin(), "zhangsan", 25);
    people.emplace(people.begin() + 1, "lisi", 30);

    return 0;
}

出力結果:

Constructing zhangsan, Age: 25
Constructing lisi, Age: 30

上記の例では、 emplace 関数は、指定された場所に Person クラスのオブジェクトを構築し、パラメーター "zhangsan" と 25 を渡すことによって、zhangsan と age 25 という名前の Person オブジェクトを構築します。次に、年齢 30 歳の lisi という名前の別の Person オブジェクトが位置 1 に構築されます。emplace はコピーまたは移動操作を回避するため、より効率的です。

2.5.9 emplace_back() C++11

template <class... Args> void emplace_back (Args&&... args);

emplace_back は std::vector のメンバー関数で、要素を構築するための引数を渡すことによって、コンテナーの最後にインプレースで新しい要素を構築します。

例:

#include <iostream>
#include <vector>

class Person {
    
    
public:
    Person(const std::string& name, int age) : name_(name), age_(age) {
    
    
        std::cout << "Constructing " << name_ << ", Age: " << age_ << std::endl;
    }

private:
    std::string name_;
    int age_;
};

int main() {
    
    
    std::vector<Person> people;

    // 使用 emplace_back 在容器末尾插入新元素
    people.emplace_back("zhangsan", 25);
    people.emplace_back("lisi", 30);

    return 0;
}

出力結果:

Constructing zhangsan, Age: 25
Constructing lisi, Age: 30

上記の例では、emplace_back 関数は、people コンテナの末尾に person クラスのオブジェクトを構築し、パラメータを渡すことで zhangsan という名前の年齢が 25 歳の person オブジェクトを構築し、次に lisi という名前の person オブジェクトを構築します。 30歳 人物オブジェクト。emplace_back はコピーまたは移動操作を回避するため、より効率的です。

2.5.10 ベクトル反復子の失敗問題

イテレータの主な機能は、アルゴリズムが基礎となるデータ構造を考慮しないようにすることです。基礎となるデータ構造は実際にはポインタであるか、ポインタがカプセル化されています。たとえば、vector のイテレータは元のエコロジー ポインタ T* です。したがって、イテレータの無効化は実際には、イテレータの下部にある対応するポインタが指すスペースが破壊され、解放されたスペースの一部が使用されることを意味し、その結果、プログラムがクラッシュします (つまり、無効なイテレータを使用し続けると、プログラムがクラッシュする可能性があります。)。
ベクトルのイテレータを無効にする可能性のある操作は次のとおりです。

1. サイズ変更、予約、挿入、割り当て、プッシュバックなど、基になる空間に変更を引き起こす操作によりイテレータが失敗する可能性があります。

#include <iostream>
using namespace std;
#include <vector>
int main()
{
    
    
	vector<int> v{
    
     1,2,3,4,5,6 };
	auto it = v.begin();
	v.assign(100, 8);
	
	while (it != v.end())
	{
    
    
		cout << *it << " ";
		++it;
	}
	cout << endl;
	return 0;
}

ここに画像の説明を挿入
エラーの理由:

上記の操作はベクトルの拡張につながる可能性があります、つまり、ベクトルの基礎となる原理の古いスペースが解放され、印刷時にはリリース間の古いスペースが引き続き使用されます。実際の操作 これは解放された領域の一部であり、コードの実行時にクラッシュが発生します。

解決:

上記の操作が完了した後、反復子を介してベクトル内の要素を操作し続ける場合は、それを再代入するだけで済みます。

2. 指定位置の要素の削除操作 – 消去

#include <iostream>
using namespace std;
#include <vector>
int main()
{
    
    
	int a[] = {
    
     1, 2, 3, 4 };
	vector<int> v(a, a + sizeof(a) / sizeof(int));
	// 使用find查找3所在位置的iterator
	vector<int>::iterator pos = find(v.begin(), v.end(), 3);
	// 删除pos位置的数据,导致pos迭代器失效。
	v.erase(pos);
	cout << *pos << endl; // 此处会导致非法访问
	return 0;
}

Erase が位置 pos の要素を削除した後、位置 pos の後の要素は、基になる空間に変更を引き起こすことなく前方に移動します。理論的には、イテレータは無効化されるべきではありませんが、削除後に pos がたまたま最後の要素だった場合、posがたまたま終了位置であり、終了位置に要素がない場合、pos は無効になります。したがって、ベクトル内の任意の位置にある要素を削除すると、VS はその位置にある反復子が無効であるとみなします。


3.ベクトル内のすべての偶数反復子を削除し、以下を無効にします。

int main()
{
    
    
	vector<int> v{
    
     1, 2, 3, 4 };
	auto it = v.begin();
	while (it != v.end())
	{
    
    
		if (*it % 2 == 0)
			v.erase(it);
		++it;
	}
	return 0;
}

正しく呼ばれる:

int main()
{
    
    
	vector<int> v{
    
     1, 2, 3, 4 };
	auto it = v.begin();
	while (it != v.end())
	{
    
    
		if (*it % 2 == 0)
			it = v.erase(it);
		else
			++it;
	}
	return 0;
}

4. Linux では、g++ コンパイラは反復子の失敗の検出においてそれほど厳密ではなく、その処理は vs の処理ほど極端ではありません。

//扩容之后,迭代器已经失效了,程序虽然可以运行,但是运行结果已经不对了
int main()
{
    
    
	vector<int> v{
    
     1,2,3,4,5 };
	for (size_t i = 0; i < v.size(); ++i)
		cout << v[i] << " ";
	cout << endl;
	auto it = v.begin();
	cout << "扩容之前,vector的容量为: " << v.capacity() << endl;
	// 通过reserve将底层空间设置为100,目的是为了让vector的迭代器失效
	v.reserve(100);
	cout << "扩容之后,vector的容量为: " << v.capacity() << endl;
	// 经过上述reserve之后,it迭代器肯定会失效,在vs下程序就直接崩溃了,但是linux下不会
	// 虽然可能运行,但是输出的结果是不对的
	while (it != v.end())
	{
    
    
		cout << *it << " ";
		++it;
	}
	cout << endl;
	return 0;
}

プログラム出力:

1 2 3 4 5
扩容之前,vector的容量为: 5
扩容之后,vector的容量为 : 100
0 2 3 4 5 409 1 2 3 4 5
//erase删除任意位置代码后,linux下迭代器并没有失效
// 因为空间还是原来的空间,后序元素往前搬移了,it的位置还是有效的
#include <vector>
#include <algorithm>
int main()
{
    
    
	vector<int> v{
    
     1,2,3,4,5 };
	vector<int>::iterator it = find(v.begin(), v.end(), 3);
	v.erase(it);
	cout << *it << endl;
	while (it != v.end())
	{
    
    
		cout << *it << " ";
		++it;
	}
	cout << endl;
	return 0;
}

プログラムは正常に実行され、次のように出力されます。

4 4
5
// erase删除的迭代器如果是最后一个元素,删除之后it已经超过end
// 此时迭代器是无效的,++it导致程序崩溃
int main()
{
    
    
	vector<int> v{
    
     1,2,3,4,5 };
	// vector<int> v{1,2,3,4,5,6};
	auto it = v.begin();
	while (it != v.end())
	{
    
    
		if (*it % 2 == 0)
			v.erase(it);
		++it;
	}
	for (auto e : v)
		cout << e << " ";
	cout << endl;
	return 0;
}

最初のデータセットを使用すると、プログラムを実行できます。

[kingxzq@localhost]$ g++ testVector.cpp - std = c++11
[kingxzq@localhost]$ . / a.out
1 3 5

2 番目のデータセットを使用すると、最終的にプログラムがクラッシュします。

[kingxzq@localhost]$ vim testVector.cpp
[kingxzq@localhost]$ g++ testVector.cpp - std = c++11
[kingxzq@localhost]$ . / a.out
Segmentation fault

上記 3 つの例から、SGI STL ではイテレータが失敗した後、コードが必ずしもクラッシュするわけではありませんが、実行結果は明らかに間違っており、begin と end の範囲内にない場合は確実にクラッシュすることがわかります。 。

5. ベクトルと同様、文字列のイテレータも挿入+展開+消去後は無効となります。

#include <string>
void TestString()
{
    
    
	string s("hello");
	auto it = s.begin();
	// 放开之后代码会崩溃,因为resize到20会string会进行扩容
	// 扩容之后,it指向之前旧空间已经被释放了,该迭代器就失效了
	// 后序打印时,再访问it指向的空间程序就会崩溃
	//s.resize(20, '!');
	while (it != s.end())
	{
    
    
		cout << *it;
		++it;
	}
	cout << endl;
	it = s.begin();
	while (it != s.end())
	{
    
    
		it = s.erase(it);
		// 按照下面方式写,运行时程序会崩溃,因为erase(it)之后
		// it位置的迭代器就失效了
		// s.erase(it);
		++it;
	}
}

イテレータの無効化に対する解決策: イテレータを使用する前に再割り当てするだけです。

エピローグ

興味のある友達は作者に注目してください、内容が良いと思われる場合は、ワンクリックトリプルリンクをお願いします、カニカニ!
作るのは簡単ではありません、不正確な点があればご指摘ください
ご訪問ありがとうございます、UUを見て頑張るモチベーションになっています
時間をきっかけに、お互いより良い人間になろう!

おすすめ

転載: blog.csdn.net/kingxzq/article/details/132078597