【C++スキルツリー】ベクタークラス解析とシミュレーション実装

画像

こんにちは、プペウアです。普段はC言語、C++、データ構造アルゴリズムを中心に更新しています...興味があればbuaフォローしてください!

画像

0. ベクターの紹介

Vectorは、さまざまな種類のシーケンス コンテナーに対応できる動的配列コンテナーです。これを配列と呼ぶことは、次のことを意味します。 **前のシーケンス テーブルと同様に、添字を使用してアクセスすることもできます。** したがって、 Vector がスペースを割り当てるときは、システムにスペースを適用するコストが比較的高いため、使用した分だけ割り当てるわけではなく、より多くのスペースを割り当てることになります。

1.ベクトル共通インターフェース

String は以前に各インターフェイスを詳細に解釈しているため、標準 STL では、各インターフェイスの受信パラメータと戻り値はほとんど同じです。したがって、この章ではインターフェイスには焦点を当てません。忘れた方はこちらの記事 [文字列解析] ( (メッセージ数45件) [C++ スキルツリー] 文字列クラス解析とシミュレーションの実装_ppeua's blog-CSDN blog )

ベクトルの宣言メソッド。T はテンプレートです。

vector<T>v;

1.1コンストラクター コンストラクター

(コンストラクター) コンストラクター宣言 インターフェースの説明
ベクター() パラメータ構築なし
ベクトル(size_type n, const value_type& val = value_type()) n va の構築と初期化
ベクトル (const ベクトル& x); コピー構築
ベクトル (InputIterator が最初、InputIterator が最後); イテレータを使用して構築を初期化する

コードに直接移動して、Vector コンストラクターの使用方法を確認します。

  1. デフォルトのコンストラクター:
vector (const allocator_type& alloc = allocator_type());

このうち、const allocator_type& alloc = allocator_type() は、アライメントやバッファ プールなどの特別なメモリ管理動作を実装するためのカスタム アロケータを作成するために使用されます。C ++ の初心者の段階では、デフォルトのメモリ割り当てを使用するだけで済みます。ストラテジー。

したがって、デフォルトのコンストラクターは次のように使用します

vector<T>v;
vector<int>v;
  1. パディングコンストラクター:

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

    コンテナに n 個の値を入力します

    vector<int>v(5,1);
    
  2. イテレータ コンストラクタを使用します

template <class InputIterator>
         vector (InputIterator first, InputIterator last,
                 const allocator_type& alloc = allocator_type());
vector<int>v(5,3);
vector<int>v1(v.begin(),v.end());

まず、充填構造を使用して、v に 5 つの 3 を充填します。次に、反復子コンストラクターを使用して、begin() ~ end() の範囲の内容を v1 に入れます。

  1. コンストラクターをコピーします:
vector (const vector& x);

1.2 イテレータ

イテレータの使用 インターフェースの説明
begin()+end() 最初のデータ位置の iterator/const_iterator を取得、最後のデータの次の位置の iterator/const_iterator を取得
rbegin()+rend() 最後のデータ位置の reverse_iterator を取得、最初のデータの前の位置の reverse_iterator を取得

**これは [begin(), end()] と [rbegin(), rend()] の構造図です。 **イテレータが使用されるシナリオでは、それは [begin(), end()) です。 left Close right open モード (つまり、左には移動できますが、右には移動できません)

並べ替えて見つける

これら 2 つの関数は、algorithmで指定された関数です。

探す

InputIterator find (InputIterator first, InputIterator last, const T& val);

指定された間隔で val を検索し、それを見つけたらその反復子を返します。実装は次のようになります。間隔を走査して val を検索します。

template<class InputIterator, class T>
  InputIterator find (InputIterator first, InputIterator last, const T& val)
{
    
    
  while (first!=last) {
    
    
    if (*first==val) return first;
    ++first;
  }
  return last;
}
vector<int>v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
cout<<*find(v.begin(),v.end(),3);

出力は次のとおりです: 3

選別

 void sort (RandomAccessIterator first, RandomAccessIterator last);

指定した間隔でデータを並べ替えます。デフォルトでは、並べ替えに < が使用されます (同じ要素が安定した並べ替えであるとは保証されません。つまり、元のデータの位置が変更されます)。

vector<int>v;
v.push_back(4);
v.push_back(1);
v.push_back(3);
v.push_back(2);
sort(v.rbegin(),v.rend());
for(auto s:v)
    cout<<s;

逆反復子を使用して、最大から最小の順に並べ替えます。

出力結果は次のとおりです: 4321

1.3 容量関連のインターフェース

容量スペース インターフェースの説明
サイズ データ数を返す
容量 現在占有されているスペースのサイズを返します。
戻り値は空です
サイズ変更 サイズを変更する
予約 容量を変更する

stringと同じなのでここでは詳しく説明しません

1.4 関連インターフェースの変更

変更 インターフェースの説明
プッシュバック テールプラグ
ポップバック 末尾削除
入れる 入れる
消す 消去
スワップ 2 つのベクトル空間を交換する
オペレーター[] 配列のようにアクセスする

ここでは、消去と挿入に焦点を当てる必要があります。これらの機能は、現在のイテレータの位置を介して要素を挿入または削除することです。

ただし、前のイテレータを直接使用すると、イテレータの失敗という問題が発生する可能性があります。本質的な理由は、自分のものではなくなったイテレータにアクセスしたことです。

ベクトルの基本的な原理は、古いスペースが解放され、印刷時にはリリース間の古いスペースが引き続き使用されるということです。it イテレーターを操作するとき、実際の操作は解放されたスペースの一部であり、これにより実行するコードが折りたたまれます。解決策:上記の操作が完了した後、反復子を介してベクトル内の要素を操作し続ける場合は、反復子を再割り当てするだけで済みます。

2. ベクトルシミュレーションの実装

Vector は String に似ており、ストレージ領域の範囲を表すために使用される 3 つのプライベート プロパティもあります(STL ソース コードは異なる方法で実装されている可能性がありますが、考え方は同じです)。

そして、最初にイテレータを宣言します(それは単に型ポインタとして見ることができます)。

ここに画像の説明を挿入

typedef T* iterator;
typedef const T* const_iterator;
private:
	_start;
	_finish;
	_endofstorage;

2.1 コンストラクター

2.1.1 デフォルトのパラメータコンストラクタがない

vector():
	_start(nullptr),
	_finish(nullptr),
	_endofstorage(nullptr)
{
    
    
    
}

これは元のコンストラクターであり、初期化リストを使用して構築する必要がありますが、C++11 にパッチが適用された後は、プライベート プロパティの場合にデフォルトのパラメーターを与えることができます。

private:
	_start=nullptr;
	_finish=nullptr;
	_endofstorage=nullptr;

現時点では、デフォルトのコンストラクターは何も記述する必要はありません。

vector():
{
    
    
    
}

2.1.2 パラメータ付きのデフォルトのコンストラクタ

  1. 塗りつぶされた形状コンストラクター:

    vector(size_t n, const T& val = T())
    {
          
          
        resize(n, val);
    }
    vector(int n, const T& val = T())
    {
          
          
        resize(n, val);
    }
    template<class InputIterator>
    vector(InputIterator begin, InputIterator end)
    {
          
          
        while (begin != end)
        {
          
          
            push_back(*begin);
            begin++;
        }
    }
    

    2 番目の仮パラメータはデフォルトのパラメータを与えます。C++ では、各型をクラスとしてみなすことができます。このとき、そのデフォルトのコンストラクタはクラス名 () です。たとえば、

    int()、double()、string()…

    1 つ目と 2 つ目は本質的に同じことを実現していますが、反復子の追加が原因です。以下の構築スキームを使用する場合

    vector<int>v(1,5);
    

    私たちが指定した構築パラメータは、同じ型の 2 つです。構築の初期化に最初の型を使用させるつもりでしたが、型が同じであるため、多くの場合、初期化に反復子を使用して 3 番目の型として認識されます。

    したがって、int を個別に処理する必要があるため、2 番目のものが存在します。

  2. 構築にはベクトル型を使用します。

    vector(const vector<T>& v)
    {
          
          
        _start = new T[v.capacity()];
        for (size_t i = 0; i < v.size(); i++)
        {
          
          
            _start[i] = v._start[i];
        }
        //_finish = v._finish; 只是令地址相等
        _finish = _start + v.size();
        _endofstorage = _start + v.capacity();
    }
    

    クラスのメンバー関数では、クラスのプライベート メンバー変数に直接アクセスできます

    ここでは Memcpy を使用できません: memcpy はメモリをコピーするため、組み込み型の場合はタスクを完了できますが、カスタム型の場合は浅いコピーになるため、= を呼び出してオーバーロードを実行して完了する必要がありますコピー
    ここに画像の説明を挿入

    全体的な考え方は、まず start に新しいスペースを割り当て、次に仮パラメータの内容を順番にコピーし、最後に長さとスペースのサイズを計算します (ここでのfinish は直接割り当てることができないアドレスであることに注意してください)。

2.2 デストラクタ

要求されたすべてのスペースをシステムに返します。

~vector()
{
    
    
    if (_start)
    {
    
    
        delete[]_start;
        _start = _finish = _endofstorage = nullptr;
    }
}

2.3 イテレータ

iterator begin()
{
    
    
    return _start;
}
iterator end()
{
    
    
    return _finish;
}
const_iterator begin()const
{
    
    
    return _start;
}
const_iterator end()const
{
    
    
    return _finish;
}

2.4 容量関連のインターフェース

size_t capacity()const
{
    
    
    return _endofstorage - _start;
}
size_t size()const
{
    
    
    return _finish - _start;
}

サイズ変更():

 void resize(size_t n, const T& val)
        {
    
    
            if (n < size())
            {
    
    
                _finish = _start + n;
            }
            else
            {
    
    
                reserve(n);
                while (_finish != _start + n)
                {
    
    
                    *_finish = val;
                    _finish++;
                }
            }
        }
 void reserve(size_t n)
        {
    
    
            if (n > capacity())
            {
    
    
                size_t sz = size();
                T* tmp = new T[n];
                if (_start)
                {
    
    
                    memcpy(tmp, _start, sizeof(T) * size());
                    delete[] _start;
                }
                _start = tmp;
                _finish = _start + sz;
                _endofstorage = _start + n;
            }
        }

これは string クラスとほぼ同じなので、あまり説明する必要はありません。

2.5 スワップ()

void swap(vector<T>v)
{
    
    
    std::swap(_start, v._start);
    std::swap(_finish, v._finish);
    std::swap(_endofstorage, v._endofstorage);
}

2.6演算子 =

vector<T>& operator= (const vector<T>& v)
{
    
    
    swap(v);
    return *this;
}

2.7 プッシュバック()

void push_back(const T& x)
{
    
    
    if (_finish == _endofstorage)
    {
    
    
        reserve(capacity() == 0 ? 1 : capacity() * 2);
    }
    *_finish = x;
    _finish++;
}

2.8 挿入()

iterator insert(iterator pos, const T& val)
{
    
    
    assert(pos < _finish&& pos >= _start);
    if (_finish == _endofstorage)
    {
    
    
        size_t newpos = pos - _start;
        reserve(capacity() == 0 ? 1 : capacity() * 2);
        pos = _start + newpos;
    }
    iterator end = _finish - 1;
    while (end >= pos)
    {
    
    
        *(end + 1) = end;
        end--;
    }
    *pos = val;
    ++_finish;
    return pos;

}

2.9 消去()

iterator erase(iterator pos)
{
    
    
    assert(pos < _finish&& pos >= _start);
    iterator it = pos + 1;
    while (it != _finish)
    {
    
    
        *(it - 1) = *it;
        it++;
    }
    _finish;
    return pos;
}

2.10 ポップバック()

void pop_back()
{
    
    
    erase(--end());
}

2.11 オペレーター []

T& operator[](size_t pos)
{
    
    
    assert(pos < size());

    return _start[pos];
}

const T& operator[](size_t pos) const
{
    
    
    assert(pos < size());

    return _start[pos];
}

pos < _finish&& pos >= _start);
イテレータ it = pos + 1;
while (it != _finish)
{ *(it - 1) = *it; それ++; } _仕上げ; 返却位置; }






## 2.10 pop_back()

~~~cpp
void pop_back()
{
    erase(--end());
}

2.11 オペレーター []

T& operator[](size_t pos)
{
    
    
    assert(pos < size());

    return _start[pos];
}

const T& operator[](size_t pos) const
{
    
    
    assert(pos < size());

    return _start[pos];
}

おすすめ

転載: blog.csdn.net/qq_62839589/article/details/131859153