stlソースコード03の分析-ベクトル

1つ、ベクトルの概要

  • ベクトルの構文については、次の記事を参照してください:https //blog.csdn.net/qq_41453285/article/details/105483009
  • 一般的に:ベクトルは可変サイズの配列です
  • 特徴:
    • 高速ランダムアクセスをサポートします。テールを超えた要素の挿入または削除は遅くなる可能性があります
    • 要素は連続したメモリ空間に格納されるため、添え字の値は非常に高速です
    • コンテナの中央で要素を追加または削除するには、非常に時間がかかります。
    • ベクトルメモリが不足すると、メモリを再適用した後、元のベクトルに関連するポインタ、参照、およびイテレータが無効になります。メモリの再割り当てには時間がかかります
  • 通常、ベクターを使用するのが最善の選択です。特別な要件がない場合は、ベクターを使用するのが最適です。
  • 他のコンテナとの比較:
ベクター 可変サイズ配列高速ランダムアクセスをサポートします。テールを超えた要素の挿入または削除は遅くなる可能性があります
そして Deque高速ランダムアクセスをサポートします。最初と最後の挿入/削除は高速です
リスト 二重リンクリスト双方向の順次アクセスのみをサポートします。リスト内の任意の位置での挿入と削除は高速です
forward_list 単一リンクリスト一方向の順次アクセスのみをサポートします。リンクリストの任意の位置での挿入および削除操作は高速です
アレイ 固定サイズの配列高速ランダムアクセスをサポートします。要素を追加または削除できません
ストリング vector似ていますが、文字の格納専用のコンテナです。ランダムアクセスは高速です。テールでの高速挿入または削除

2、ベクトル定義の要約

  • ベクトルは<stl_vector.h>ヘッダーファイルに設定されます
//alloc是SGI STL的空间配置器

template <class T, class Alloc = alloc>

class vector {

public:

    // vector 的嵌套类型定义

    typedef T value_type;

    typedef value_type* pointer;

    typedef value_type* iterator;

    typedef value_type& reference;
    
    typedef size_t size_type;

    typedef ptrdiff_t difference_type;


protected:

    // simple_alloc是SGI STL的空间配置器,见前面空间适配器文章的介绍

    typedef simple_alloc<value_type, Alloc> data_allocator;

    iterator start; // 表示目前使用空间的头

    iterator finish; // 表示目前使用空间的尾

    iterator end_of_storage; // 表示目前可用空间的尾


    void insert_aux(iterator position, const T& x);

    void deallocate() {

    if (start)

        data_allocator::deallocate(start, end_of_storage - start);

    }


    void fill_initialize(size_type n, const T& value) {

    start = allocate_and_fill(n, value);

    finish = start + n;

    end_of_storage = finish;

}


public:

    iterator begin() { return start; }

    iterator end() { return finish; }

    size_type size() const { return size_type(end() - begin()); }

    size_type capacity() const {

        return size_type(end_of_storage - begin());

    }

    bool empty() const { return begin() == end(); }

    reference operator[](size_type n) { return *(begin() + n); }


    vector() : start(0), finish(0), end_of_storage(0) {}

    vector(size_type n, const T& value) { fill_initialize(n,value); }

    vector(int n, const T& value) { fill_initialize(n,value); }

    vector(long n, const T&value) { fill_initialize(n,value); }

    explicit vector(size_type n) { fill_initialize(n,T()); }


    ~vector() {

        destroy(start, finish); //全局函式,见前面文章destroy函数的介绍

        deallocate(); //这是 vector的㆒个 member function

    }


    reference front() { return *begin(); } // 第一个元素

    reference back() { return *(end() - 1); } // 最后一个元素

    void push_back(const T& x) { // 将元素安插至最尾端

    if (finish != end_of_storage) {

        construct(finish, x); //全局函式,见前面文章construct函数的介绍

        ++finish;

    }

    else

        insert_aux(end(), x); //这是 vector的一个member function

    }


    void pop_back() { // 将最尾端元素取出

        --finish;

        destroy(finish); // 全局函式,见前面文章destroy函数的介绍

    }


    iterator erase(iterator position) { // 清除某位置上的元素

    if (position + 1 != end())

        copy(position + 1, finish, position); // 后续元素往前搬移

        --finish;

        destroy(finish); // 全局函式,见前面文章destroy函数的介绍

        return position;
    }


    void resize(size_type new_size, const T& x) {

    if (new_size < size())

        erase(begin() + new_size, end());

    else

        insert(end(), new_size - size(), x);

    }

    void resize(size_type new_size) { resize(new_size, T()); }

    void clear() { erase(begin(), end()); }


protected:

    // 配置空间并填满内容

    iterator allocate_and_fill(size_type n, const T& x) {

    iterator result = data_allocator::allocate(n);

    uninitialized_fill_n(result, n, x); // 全局函式,见前面uninitialized_fill_n函数的介绍

    return result;

}

3、ベクトルイテレータ

  • ベクトルは連続線形空間を維持するため、その要素タイプに関係なく、通常のポインターをベクトルイテレーターとして使用して、必要なすべての条件を満たすことができます。
  • ベクトルイテレータでサポートされている操作は次のとおりです(通常のポインタでも使用できます)。
    • 演算子*、演算子->、演算子++、演算子-、演算子+、演算子-、演算子+ =、演算子-=
  • Vectorはランダムアクセスをサポートしており、通常のポインターにはこの機能があるため、vectorはランダムアクセスイテレーター(ランダムアクセスイテレーター)を提供します。
  • ベクトルのイテレータは次のように定義されます。
template <class T, class Alloc = alloc>

class vector {

public:

    typedef T value_type;

    typedef value_type* iterator; //vector的迭代器是原生指标

    ...

};
  • 例えば:
vector<int>::iterator ivite; //等同于int* ivite;

vector<Shape>::iterator svite; //等同于Shape* svite;

第四に、ベクトルのデータ構造

  • ベクトルのデータ構造は非常に単純です:線形連続空間
  • ベクトルの3つのデータ構造は次のとおりです。
    • start:現在使用されているスペースの先頭を示します
    • 終了:現在使用されているスペースの終わりを示します
    • end_of_storage:現在使用可能なスペースの終わりを示します
template <class T, class Alloc = alloc>

class vector {

...

protected:

    iterator start; //表示目前使用空间的头

    iterator finish; //表示目前使用空间的尾

    iterator end_of_storage; //表示目前可用空间的尾

    ...
    
};
  • 注:スペース割り当ての速度コストを削減するために、ベクトル構成の実際のサイズは、将来の拡張の可能性に対するクライアントの要求よりも大きくなる可能性があります。これが容量の概念です。言い換えると、ベクトルの容量は常にそのサイズ以上です。容量がサイズと同じになると、次に新しい要素を追加するときに新しいスペースを開く必要があります。以下に示すように

  • スタート、フィニッシュ、及びend_of_storageの三のイテレータを用いて、ベクターは、このような機能を提供開始し、等エンドラベリング、サイズ、容量、空の容器の判断、注釈[]演算子、前部要素値、および最後の要素値...として、次のとおりです。
template <class T, class Alloc = alloc>

class vector {

...

public:

    iterator begin() { return start; }

    iterator end() { return finish; }

    size_type size() const { return size_type(end() - begin()); }

    size_type capacity() const {

        return size_type(end_of_storage - begin());
    
    }

    bool empty() const { return begin() == end(); }

    reference operator[](size_type n) { return *(begin() + n); }

    reference front() { return *begin(); }

    reference back() { return *(end() - 1); }

    ...
    
};

5、ベクトル構築とメモリ管理(コンストラクター、push_back)

ベクトルメモリ管理

  • Vectorは、デフォルトでallocをスペースコンフィギュレーターとして使用し、それに応じてdata_allocatorを定義して、エレメントサイズを構成単位として使用するのをより便利にします。
template <class T, class Alloc = alloc>

class vector {

protected:

    // simple_alloc<>见前面文章介绍

    typedef simple_alloc<value_type, Alloc> data_allocator;

    ...

};
  • したがって、data_allocator :: alllocate(n)は、n個の要素スペースを構成することを意味します

コンストラクタ

  • Vectorには多くのコンストラクターがあり、そのうちの1つでスペースのサイズと初期値を指定できます。
//构造函数,允许指定vector大小n和初值value

vector(size_type n, const T& value) { fill_initialize(n, value); }


// 充填并予初始化

void fill_initialize(size_type n, const T& value) {

    start = allocate_and_fill(n, value);

    finish = start + n;

    end_of_storage = finish;

}


// 配置而后充填

iterator allocate_and_fill(size_type n, const T& x) {

    iterator result = data_allocator::allocate(n); //配置n个元素空间

    uninitialized_fill_n(result, n, x); //全局函式,会根据第1个参数类型特性决定使用算法fill_n()或反复调用construct()来完成任务

    return result;

}

 

push_back()関数

  • push_back()を使用してベクトルの最後に新しい要素を挿入すると、関数は最初にスペアスペースがあるかどうかを確認し、ある場合はスペアスペースに直接要素を作成し、イテレーターの終了を調整してより大きなベクトル。空き領域がない場合は、領域を拡張します(再構成、データの移動、元の領域の解放)
  • push_back()のプロトタイプは次のとおりです。
void push_back(const T& x) {

    if (finish != end_of_storage) { //还有备用空间

        construct(finish, x); //全局函式
    
        ++finish; //调整水位高度

    }
    
    else //已无备用空间

        insert_aux(end(), x); // vector member function,见下

}
template <class T, class Alloc>

void vector<T, Alloc>::insert_aux(iterator position, const T& x) {

    if (finish != end_of_storage) { //还有备用空间

        // 在备用空间起始处建构一个元素,并以 vector 最后一个元素值为其初值。

        construct(finish, *(finish - 1));

        // 调整水位。

        ++finish;

        T x_copy = x;

        copy_backward(position, finish - 2, finish - 1);

        *position = x_copy;

    }

    else { // 已无备用空间

        const size_type old_size = size();

        const size_type len = old_size != 0 ? 2 * old_size : 1;

        // 以上配置原则:如果原大小为0,则配置 1(个元素大小)

        // 如果原大小不为 0,则配置原大小的两倍,

        // 前半段用来放置原数据,后半段准备用来放置新数据

        iterator new_start = data_allocator::allocate(len); // 实际配置

        iterator new_finish = new_start;

        try {

            // 将原 vector 的内容拷贝到新vector

            new_finish = uninitialized_copy(start, position, new_start);

            // 为新元素设定初值 x
        
            construct(new_finish, x);

            // 调整水位

            ++new_finish;
    
            // 将原vector的备用空间中的内容也忠实拷贝过来

            new_finish = uninitialized_copy(position, finish, new_finish);

        }

        catch(...) {

            // "commit or rollback" semantics.

            destroy(new_start, new_finish);

            data_allocator::deallocate(new_start, len);

            throw;

        }

        //析构并释放原vector
    
        destroy(begin(), end());

        deallocate();


        // 调整迭代器,指向新vector
    
        vector start = new_start;

        finish = new_finish;

        end_of_storage = new_start + len;

    }

}

 

6、ベクトルメモリ再配布戦略

  • ベクトルメモリ再割り当て戦略:
    • ベクトルは配列の形で格納されます。ベクトルに要素を追加するときに、ベクトルの容量が不十分な場合、ベクトルは拡張されます。
    • 拡張のルールは、元のスペースの後に新しいスペースを接続するのではなく(元のスペースの後に構成に使用できるスペースがまだあるという保証がないため)、現在のスペースよりも大きい新しいメモリスペースを申請することです( gccとvcのアプリケーションルールは異なります(以下の概要を参照)。次に、元のメモリの内容を新しいメモリにコピーしてから、元のメモリを解放します。
    • 重要: gccとvcの環境では、vectorの展開ルールが異なります
  • 注(強調): ベクトルに対する操作は、スペースが再割り当てされると、元のベクトルを指すすべてのイテレーターが無効になりますこれはプログラマーにとって簡単な間違いなので、注意してください

ウィンドウズ

  • ベクトルメモリの再分配、つまり容量の増加は規則的であり、次の式で表すことができます。
maxSize = maxSize + ((maxSize >> 1) > 1 ? (maxSize >> 1) : 1)
  • 図:
    • 1、2、3、4、6、9、13、19から順次増加しています。
    • 4から始まるルールがあります。現在のインデックスの値は、前の要素の値と前の要素の値の合計に等しくなります。

  • テスト手順は次のとおりです。
#include <iostream>

#include <vector>


using namespace std;


int main()

{

    std::vector<int> iv;

    iv.push_back(1);


    cout << iv.capacity() << endl; //1


    iv.push_back(1);

    cout << iv.capacity() << endl; //2


    iv.push_back(1);
    
    cout << iv.capacity() << endl; //3


    iv.push_back(1);

    cout << iv.capacity() << endl; //4


    iv.push_back(1);

    cout << iv.capacity() << endl; //6


    iv.push_back(1);


    iv.push_back(1);

    cout << iv.capacity() << endl; //9


    return 0;

}
  • 操作効果は以下のとおりです。 

Linux下

  • Linuxでの拡張ルールは次のとおりです。比較的単純です。つまり、サイズを元のサイズの2倍に拡張します。
maxSize = maxSize*2;
  • イラスト: 1、2、4、8、16から増加しています...順次

  • テスト手順は次のとおりです。
#include <iostream>

#include <vector>


using namespace std;


int main()

{

    std::vector<int> iv;

    iv.push_back(1);


    cout << iv.capacity() << endl; //1


    iv.push_back(1);
    
    cout << iv.capacity() << endl; //2


    iv.push_back(1);

    cout << iv.capacity() << endl; //4


    iv.push_back(1);
    
    cout << iv.capacity() << endl; //4


    iv.push_back(1);

    cout << iv.capacity() << endl; //8


    iv.push_back(1);


    iv.push_back(1);

    cout << iv.capacity() << endl; //8


    return 0;

}
  • 操作効果は以下のとおりです。 

7つのベクトル要素操作(pop_back、erase、clear、insert)

  • 提供されている要素操作アクションはたくさんあるので、この記事ではそれらを一つずつ説明します。

pop_back

//将尾端元素拿掉,并调整大小

void pop_back() {

    --finish; //将尾端标记往前移一格,表示将放弃尾端元素

    destroy(finish); // destroy是全局函式

}

 

消去

// 清除[first,last)中的所有元素

iterator erase(iterator first, iterator last) {

    iterator i = copy(last, finish, first); //copy是全局函式

    destroy(i, finish); //destroy是全局函式

    finish = finish - (last - first);

    return first;

}
  • 下の図は、上の消去機能のバージョンです。

 

// 清除某个位置上的元素

iterator erase(iterator position) {

    if (position + 1 != end())

        copy(position + 1, finish, position); //copy是全局函式

    --finish;

    destroy(finish); //destroy是全局函式

    return position;

}

 

晴れ

//清除容器内所有元素

void clear() { erase(begin(), end()); }

 

インサート

//从position开始,插入n个元素,元素初值为x

template<class T,class Alloc>

void vector<T, Alloc>::insert(iterator position, size_type n, const T& x)

{

    if (n != 0) { //当n!= 0才进行以下所有动作

        if (size_type(end_of_storage - finish) >= n){

            //备用空间大于等于“新增元素个数”

            T x_copy = x;

            // 以下计算插入点之后的现有元素个数

            const size_type elems_after = finish - position;

            iterator old_finish = finish;

            if (elems_after > n){

                //“插入点之后的现有元素个数”大于“新增元素个数”

                uninitialized_copy(finish - n, finish, finish);

                finish += n; // 将vector尾端标记后移

                copy_backward(position, old_finish - n, old_finish);

                fill(position, position + n, x_copy); //从插入点开始填入新值

            }

        }

        else {

            //“插入点之后的现有元素个数”小于等于“新增元素个数”

            uninitialized_fill_n(finish, n - elems_after, x_copy);

            finish += n - elems_after;

            uninitialized_copy(position, old_finish, finish);

            finish += elems_after;

            fill(position, old_finish, x_copy);

        }

    }

    else {

        // 备用空间小于“新增元素个数”(那就必须配置额外的内存)

        // 首先决定新长度:旧长度的两倍,或旧长度+新增元素个数

        const size_type old_size = size();

        const size_type len = old_size + max(old_size, n);

        // 以下配置新的vector空间

        iterator new_start = data_allocator::allocate(len);

        iterator new_finish = new_start;


        STL_TRY {

            // 以下首先将旧vector的安插点之前的元素复制到新空间

            new_finish = uninitialized_copy(start, position, new_start);

            // 以下再将新增元素(初值皆为n)填入新空间

            new_finish = uninitialized_fill_n(new_finish, n, x);

            // 以下再将旧 vector 的插入点之后的元素复制到新空间

            new_finish = uninitialized_copy(position, finish, new_finish);

        }

# ifdef STL_USE_EXCEPTIONS

        catch(...) {

            // 如有异常发生,实现"commit or rollback" semantics.

            destroy(new_start, new_finish);

            data_allocator::deallocate(new_start, len);

            throw;
    
        }

# endif /* STL_USE_EXCEPTIONS */


        // 以下清除并释放旧的vector
    
        destroy(start, finish);

        deallocate();

        // 以下调整水位标记

        start = new_start; finish =

        new_finish; end_of_storage =

        new_start + len;

    }

}
  • 挿入が完了すると、新しいノードはセンチネルイテレータ(挿入ポイントを示す前のボタンの位置)が指すノードの前にあることに注意してください。これは「挿入操作」のSTL標準仕様です。下の図は、insert(position、n、x)の操作を示しています。

スペアスペース> =新しい要素の数の場合:

  • ①空きスペース2> =新しい要素の数2
  • ②挿入点以降の既存要素数3>新規要素数2

  • ③挿入点以降の既存要素数2 <=新規要素数3

スペアスペース> =新しい要素の数の場合:

  • たとえば、次のスペアスペース2 <新しい要素の数n == 3

おすすめ

転載: blog.csdn.net/www_dong/article/details/113825932