1. 程式人生 > >C++ 學習筆記之——STL 庫 vector

C++ 學習筆記之——STL 庫 vector

vector 是一種順序容器,可以看作是可以改變大小的陣列。

就像陣列一樣,vector 佔用連續的記憶體地址來儲存元素,因此可以像陣列一樣用偏移量來隨機訪問,但是它的大小可以動態改變,容器會自動處理記憶體分配問題。

在內部,vector 使用動態分配的陣列來儲存元素,當新元素插入時,如果現有的儲存空間已經佔滿,則需要重新再分配一個新的陣列,並且將之前的元素都移動到新的記憶體上。這個過程是非常耗時的,因此,vector 並不會在每次插入新元素時都重新分配記憶體。

相反,vector 容器可能會分配一些額外的記憶體來適應其大小的增長,因此,其真實容量可能比儲存這些元素實際需要的記憶體要大。庫通過不同的策略來平衡記憶體佔用和空間再分配,但無論如何,空間分配只應在 vector 大小以對數增長的時候發生,以便在向量末尾插入單個元素可以做到均攤情況下是常數級的時間複雜度

因此,相對於陣列,vector 會消耗更多的記憶體來換取更有效地對記憶體進行管理並且動態增長。

相對於其他動態容器,vector 支援隨機訪問,並且能相對高效地在末尾插入或者刪除元素,但如果要在其他位置插入或者刪除元素,vector 就會表現得很差,而且迭代器和引用也不是那麼方便。

建構函式

  • explicit vector (const allocator_type& alloc = allocator_type()); 預設建構函式,構造出一個不包含任何元素的空的 vector;
  • explicit vector (size_type n); 構造出一個包含 $n$ 個元素的 vector,預設會初始化為 0;
  • `explicit vector (size_type n, const value_type& val,

       const allocator_type& alloc = allocator_type());` 構造出一個包含 $n$ 個值為 $val$ 的 vector;
    
  • `vector (InputIterator first, InputIterator last,
    const allocator_type& alloc = allocator_type());` 構造出一個包含迭代器 $[first, end)$ 範圍內元素的 vector,注意左閉右開;
  • vector (const vector& x); 複製建構函式,構造出一個和 $x$ 相同的 vector;
#include <iostream>
#include <vector>

using namespace std;

int main ()
{
    vector<int> first;                                 // 空的 vector
    vector<int> second (4, 100);                       // 包含 4 個值為 100 元素的 vector,[100, 100, 100, 100]
    vector<int> third (second.begin(), second.end()); // 包含 second 起始迭代器到終止迭代器區間元素的 vector,[100, 100, 100, 100]
    vector<int> fourth (third);                       // 對 third 的複製,[100, 100, 100, 100]

    // 陣列也可以用來作為迭代器初始化 vector
    int myints[] = {16, 2, 77, 29};
    vector<int> fifth (myints, myints + sizeof(myints) / sizeof(int) ); //[16, 2, 77, 29]
    vector<int> sixth (4); // [0, 0, 0, 0]

    cout << "The contents of fifth are:";
    for (vector<int>::iterator it = fifth.begin(); it != fifth.end(); ++it)
        cout << ' ' << *it;
    cout << '\n';

    return 0;
}

賦值運算

賦值運算會給容器賦予新的內容,替換掉舊的內容,同時改變其大小。

#include <iostream>
#include <vector>

using namespace std;

int main ()
{
    vector<int> foo (3,0);
    vector<int> bar (5,0);

    bar = foo;
    foo = vector<int>();

    cout << "Size of foo: " << int(foo.size()) << '\n'; // 0
    cout << "Size of bar: " << int(bar.size()) << '\n'; // 3
    return 0;
}

迭代器

  • iterator begin(); 返回指向 vector 中第一個元素的迭代器;
  • iterator end(); 返回一個迭代器,引用向量容器中的 past-the-end 元素,也即最後一個元素之後的理論元素;
  • reverse_iterator rbegin(); 返回指向 vector 中最後一個元素的反向迭代器,增加反向迭代器會使它們向前移動;
  • reverse_iterator rend(); 返回一個反向迭代器,指向向量中第一個元素之前的理論元素;
#include <iostream>
#include <vector>

using namespace std;

int main ()
{
    vector<int> myvector;
    for(int i = 0; i < 5; i++)
    {
      myvector.push_back(i);
    }

    vector<int>::iterator it = myvector.begin();

    for (; it != myvector.end(); it++)
    {
      cout << *it << '\t';
    }
    cout << endl;

    vector<int>::reverse_iterator rit = myvector.rbegin();

    for (; rit != myvector.rend(); rit++)
    {
      cout << *rit << '\t';
    }
    cout << endl;

    return 0;
}
// 0    1   2   3   4
// 4    3   2   1   0

也可以對向量建立指標,然後通過指標來訪問成員函式。或者建立引用。

#include <iostream>
#include <vector>

using namespace std;

int main ()
{
    vector<int> myvector;
    for(int i = 0; i < 5; i++)
    {
      myvector.push_back(i);
    }

    vector<int> *p = &myvector;
    p->push_back(5);

    vector<int>::reverse_iterator rit = p->rbegin();
    //   vector<int>::reverse_iterator rit = (*p).rbegin();


    for (; rit != p->rend(); rit++)
    {
      cout << *rit << '\t';
    }
    cout << endl;

    vector<int> &ref_myvector = myvector;
    ref_myvector.push_back(6);

    vector<int>::iterator it = ref_myvector.begin();

    for (; it != ref_myvector.end(); it++)
    {
      cout << *it << '\t';
    }
    cout << endl;

    return 0;
}
// 5    4    3   2   1   0
// 0    1   2   3   4   5   6

容量

  • size_type size() const; 返回向量中元素的個數;
  • size_type max_size() const; 返回向量中最大可能包含的元素個數,但這只是理論上的;
  • void resize (size_type n, value_type val = value_type()); 重新設定向量的大小使之包含 $n$ 個元素;如果 $n$ 小於現有向量大小,則只保留前 $n$ 個元素;如果 $n$ 大於現有向量大小,那麼在末尾插入元素來使向量大小達到 $n$ ;如果 $n$ 大於現有向量容量,那麼會自動重新分配記憶體;
  • size_type capacity() const; 返回向量當前分配的記憶體可以包含多少個元素;
  • bool empty() const; 返回當前向量是否為空,也就是大小是否為零;
  • void reserve (size_type n); 讓向量當前分配的記憶體至少可以包含 $n$ 個元素;
#include <iostream>
#include <vector>

using namespace std;

int main ()
{
    vector<int> myvector;
    cout << "max_size: " << myvector.max_size() << endl;

    // 新增元素的過程中容量會不斷增大
    for(int i = 0; i < 10; i++)
    {
        myvector.push_back(i);
        cout << "size: " << myvector.size() << '\t';
        cout << "capacity: " << myvector.capacity() << endl;
    }

    vector<int> othervector;
    othervector.reserve(100);
    // 新增元素的過程中大小不超過 100 就不會增大
    for(int i = 0; i < 10; i++)
    {
        othervector.push_back(i);
        cout << "size: " << othervector.size() << '\t';
        cout << "capacity: " << othervector.capacity() << endl;
    }

    return 0;
}

元素訪問

  • reference operator[] (size_type n); 像陣列一樣訪問位置 $n$ 處的元素,但不會進行邊界檢測;
  • reference at (size_type n); 訪問位置 $n$ 處的元素,但會進行邊界檢測;
  • reference front(); 返回向量中第一個元素的引用;
  • reference back(); 返回向量中最後一個元素的引用;
#include <iostream>
#include <vector>

using namespace std;

int main ()
{
    vector<int> myvector;

    for(int i = 0; i < 10; i++)
    {
        myvector.push_back(i);

    }

    cout << myvector.front() << endl;
    cout << myvector.back() << endl;

    // 此處越界訪問向量,不會提示
    for(int i = 0; i <= myvector.size(); i++)
    {
        cout << myvector[i] << '\t';
    }
    cout << endl;

    // 此處越界訪問向量,會丟擲一個 out_of_range 異常
    for(int i = 0; i <= myvector.size(); i++)
    {
        cout << myvector.at(i) << '\t';
    }
    cout << endl;

    return 0;
}

向量修改

  • void assign (InputIterator first, InputIterator last); 給向量重新分配迭代器 $[first, end)$ 範圍內的元素,注意左閉右開;
  • void assign (size_type n, const value_type& val); 給向量重新分配 $n$ 個值 $val$ 的元素;
  • void push_back (const value_type& val); 在向量末尾新增一個元素;
  • void pop_back(); 從向量末尾刪除一個元素;
  • iterator insert (iterator position, const value_type& val); 在迭代器位置前面插入一個元素,返回指向第一個新插入元素的迭代器
  • void insert (iterator position, size_type n, const value_type& val); 在迭代器位置前面插入 $n$ 個值 $val$ 的元素;
  • void insert (iterator position, InputIterator first, InputIterator last); 在迭代器位置前面插入迭代器 $[first, end)$ 範圍內的元素;
  • iterator erase (iterator position); 刪除迭代器位置的元素,返回最後一個被刪除元素的後面一個元素的迭代器
  • iterator erase (iterator first, iterator last); 刪除迭代器 $[first, end)$ 範圍內的元素,返回最後一個被刪除元素的後面一個元素的迭代器
  • void swap (vector& x); 和向量 $x$ 進行交換,兩個向量元素型別相同,但大小可能不同;
  • void clear(); 清空向量;
#include <iostream>
#include <vector>

using namespace std;

int main ()
{
    vector<int> first;
    vector<int> second;
    vector<int> third;

    first.assign(7, 100);             // [100, 100, 100, 100, 100, 100, 100]

    vector<int>::iterator it;
    it = first.begin() + 1;

    second.assign(it, first.end() - 1); // [100, 100, 100, 100, 100]

    int myints[] = {1776, 7, 4};
    third.assign(myints, myints + 3);   // [1776, 7, 4]

    cout << "Size of first: " << int (first.size()) << '\n';
    cout << "Size of second: " << int (second.size()) << '\n';
    cout << "Size of third: " << int (third.size()) << '\n';

    vector<int> myvector (3, 100); // [100, 100, 100]

    it = myvector.begin() + 1;
    it = myvector.insert(it, 200); // [100, 200, 100, 100],此時 it 指向新插入的元素 200

    myvector.insert(it, 2, 300); // [100, 300, 300, 200, 100, 100],此時 it 無效了

    it = myvector.begin();

    vector<int> anothervector (2, 400);  // [400, 400]

    myvector.insert(it + 2, anothervector.begin(), anothervector.end());
    // [100, 300, 400, 400, 300, 200, 100, 100]

    int myarray [] = {501, 502, 503};
    myvector.insert (myvector.begin(), myarray, myarray + 3);
    // [501, 502, 503, 100, 300, 400, 400, 300, 200, 100, 100]

    cout << "myvector contains:";
    for (it = myvector.begin(); it < myvector.end(); it++)
        cout << ' ' << *it;
    cout << '\n';

    myvector.clear();
    for (int i = 1; i <= 10; i++) myvector.push_back(i);
    // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

    it = myvector.erase(myvector.begin() + 5);
    // [1, 2, 3, 4, 5, 7, 8, 9, 10],此時 it 指向 6 後面的元素 7

    it = myvector.erase(myvector.begin(), myvector.begin() + 3);
    // [4, 5, 7, 8, 9, 10],此時 it 指向 3 後面的元素 4

    cout << "myvector contains:";
    for (unsigned i = 0; i < myvector.size(); ++i)
        cout << ' ' << myvector[i];
    cout << '\n';

    return 0;
}

獲取更多精彩,請關注「seniusen」!