1. 程式人生 > >順序容器--vector的詳解

順序容器--vector的詳解

前言

部落格編寫人:Willam
部落格編寫時間:2017/3/11
博主郵箱:2930526477@qq.com(有志同道合之人,可以加qq交流交流程式設計心得)

1、vector介紹

vector是一種順序容器,所謂的容器就是一個儲存一組型別相同的資料的集合。容器有順序容器和關聯容器之分,所謂的順序容器指的是元素排列次序與元素的值無關,而是由元素新增到容器裡的次序決定。

vector有如下幾個特點:


 - 支援資料的隨機存取
 - 在集合的末尾新增元素很快,為常數時間
 - 在集合中間新增元素很慢,O(n)
 - 使用模板實現了vector,所以它可以存放任意型別的資料
 - 有些容器提供了stable iterator保證,但是vector沒有,它的某些操作會造成它的iterator失效。

2、vector的標頭檔案

#include<vector>
using std::vector;

3、vector的建構函式

vector() //無參建構函式

vector(size_type num,const type & val) //構造一個初始放入num個值為val的元素的Vector

vector(const vector & form)    //構造一個與vector from 相同的vector,其實這個是拷貝建構函式

vector(input_iterator start,input_iterator end)  // 構造一個初始值為[start,end)區間元素的Vector(注:半開區間).

建構函式的使用示例:

第一種:

#include<iostream>
#include<vector>

using namespace std;

int main() {
    //定義一個使用無參構造的vector
    vector<int> test;

}

第二種:

#include<iostream>
#include<vector>

using namespace std;

int main() {
    //建立一個含有10個1 的vector
    vector<int> test(10
,1); }

第三種:

#include<iostream>
#include<vector>

using namespace std;

int main() {
    //建立一個含有10個1 的vector
    vector<int> test(10,1);
    //使用拷貝建構函式,構建一個test的複製品
    vector<int> copy(test);

}

第四種:

#include<iostream>
#include<vector>

using namespace std;

int main() {
    //建立一個含有10個1 的vector
    vector<int> test(10,1);
    //使用兩個迭代器指定的範圍內的容器的值來初始化copy
    //至於cbegin和cend後續會介紹
    vector<int> copy(test.cbegin(),test.cend());

}

如果不知道什麼迭代器,請閱讀這篇部落格:迭代器介紹

4、vector的運算子介紹

 ==   //比較兩個容器的內容是否相等,相等則返回true
 !=   //比較兩個容器的內容是否不相等,不相等返回true
 <=   //比較左邊的容器的內容是否小於等於右邊,如果是,則返回true
 >=   //比較左邊的容器的內容是否大於等於右邊,如果是,則返回true
 <    //比較左邊的容器的內容是否小於右邊,如果是,則返回true
 >    //比較左邊的容器的內容是否大於右邊,如果是,則返回true
 []   //下標操作符,用於讀取容器中特定位置的內容

vector的運算子主要就是有關兩個容器內容的比較,下面我們給出容器內容比較的一個規則:

  • 如果兩個容器內容長度不同,但是短的那個的內容和長的那個的前面的內容全部相同,那麼就是容器長度長的那個比較大
  • 如果兩個容器內容的長度相同而且內容也全部相同,則兩個容器相等
  • 除了前面的兩種情況,兩個容器的大小由他們第一個不同的位置上的元素決定。

示例如下:

#include<iostream>
#include<vector>

using namespace std;

int main() {
    //建立4個vector,使用列表初始化,相當與呼叫拷貝建構函式
    vector<int> v1 = {1,5,6,7,8};
    vector<int> v2 ={1,5};
    vector<int> v3 ={1,5,2 };
    vector<int> v4 ={1,5,6,7,8 };
    cout << "v1==v4 " << (v1 == v4) << endl;
    cout << "v1>v2  " << (v1 > v2) << endl;
    cout << "v1>v3  " << (v1 > v3) << endl;
    system("pause");
    return 0;


}

輸出:

這裡寫圖片描述

5、vector讀取元素方法

operator[]  //使用下標操作符讀取對應的容器的內容

at()       //使用at函式讀取對應容器的內容

front()   //返回容器的第一個元素的內容

back()  //返回容器的最後一個元素的內容 
  • operator[]使用示例:
#include<iostream>
#include<vector>

using namespace std;

int main() {
    //建立4個vector,使用列表初始化,相當與呼叫拷貝建構函式
    vector<int> v1 = {1,5,6,7,8};
    //輸出容器中下標為2中存放的內容,
    //下標從0開始,所就是6
    cout << v1[2] << endl; 
    //修改容器中下標為2中存放的內容因為它返回的是
    //該值的引用
    v1[2] = 10;
    cout << v1[2] << endl;
    system("pause");
    return 0;
}

輸出:
這裡寫圖片描述

  • at函式使用示例
#include<iostream>
#include<vector>

using namespace std;

int main() {
    //建立4個vector,使用列表初始化,相當與呼叫拷貝建構函式
    vector<int> v1 = {1,5,6,7,8};
    //輸出容器中下標為2中存放的內容,
    //下標從0開始,所就是6
    cout << v1.at(2) << endl;
    //修改下標為2中的內容,因為它返回的是
    //該值的引用
    v1.at(2) = 10;
    cout << v1.at(2) << endl;
    system("pause");
    return 0;
}

輸出:
這裡寫圖片描述

  • front函式和back函式的使用示例:
#include<iostream>
#include<vector>

using namespace std;

int main() {
    //建立4個vector,使用列表初始化,相當與呼叫拷貝建構函式
    vector<int> v1 = {1,5,6,7,8};
    //輸出容器中下第一個元素,
    cout << v1.front() << endl;
    //輸出容器的最後一個元素
    cout << v1.back() << endl;
    system("pause");
    return 0;
}

輸出:
這裡寫圖片描述

使用思考:
對於運算子[]和at函式的選擇,我們應該選擇at函式,這是因為at函式它添加了溢位檢查的程式碼,比[]安全,另外,再補充個小知識:at內部函式就是使用[]實現的,front和back函式分別通過(* begin())和*(end()-1)實現。

5、vector的迭代器

如果不明白迭代器,可以訪問我的另外一篇部落格:迭代器的介紹

begin()   //返回一個iterator型別的變數,它指向vector存放第一個元素變數的地址
end()     //返回一個iterator型別的變數,它指向vector最後一個元素的下一個位置

cbegin()   //返回一個const_iterator型別的變數,它指向vector存放第一個元素變數的地址
cend()    //返回一個const_iterator型別的變數,它指向vector最後一個元素的下一個位置

rbegin()   //返回一個reverse_iterator型別的變數,它指向vector最後一個元素
rend()     //返回一個reverse_iterator型別的變數,它指向vector第一個元素的前一個位置

crbegin()    //返回一個const_reverse_iterator型別的變數,它指向vector最後一個元素
crend()     //返回一個const_reverse_iterator型別的變數,它指向vector第一個元素的前一個位置

程式碼示例:

#include<iostream>
#include<vector>

using namespace std;

int main() {
    //建立4個vector,使用列表初始化,相當與呼叫拷貝建構函式
    vector<int> v1 = {1,5,6,7,8};
    cout << "begin和end的使用:" << endl;
    cout << *v1.begin() << endl; //相當於front函式
    //cout << *v1.end() << endl;  這個程式碼會出錯的
    //因為end函式指向一個不屬於v1的空間
    //下面這個才是指向v1最後一個元素
    cout << *(v1.end() - 1) << endl;


    cout << endl;
    cout << "rbegin和rend的使用:" << endl;
    cout << *v1.rbegin() << endl;
    //cout << *v1.rend() << endl;  這個程式碼會出錯的
    //因為rend函式指向一個不屬於v1的空間
    //下面這個才是指向v1翻轉後的最後一個元素
    cout << *(v1.rend()-1) << endl;

    system("pause");
    return 0;
}

輸出:
這裡寫圖片描述

6、遍歷vector容器的方法

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

int main() {
    vector<int> v1 = {1,2,3,4,5};
    //方法1
    for (int i = 0; i < v1.size(); i++) {
        cout << v1[i] << " ";
    }
    cout << endl;

    for (std::vector<int>::iterator k = v1.begin(); k < v1.end(); ++k)
    {
        cout << *k << " ";
    }
    cout << endl;

    system("pause");
    return 0;
}

輸出:
這裡寫圖片描述

對應遍歷的效率問題,可以參考我的另外一篇部落格:訪問vector元素的效率比較

7、vector新增和刪除元素的方法

push_back( )  //新增元素到當前vector末尾

pop_back()    //刪除最最尾端的元素

emplace_back() //c++ 11標準:插入一個值到vector末尾,相當於push_back

emplace()   //c++ 11 新標準:插入一個或多個值到vector內,相當於insert

insert()     //插入一個或多個元素至 vector 內的任意位置。

erase()      //刪除一個或多個元素

clear()     //清空所有元素

各個函式的具體使用示例:

  • push_back:
#include <vector>
#include <iostream>

int main()
{
/*  
使用這個函式的時候,要考慮到一種情況就是:我們的容器的迭代器會出現失效的情況,這個主要的原因就是因為
我們的在新增新的成員的時候,會出現容器的最大容量小於當前的元素的個數,這個
時候我們就要重新為vector分配新的空間,從而會造成之前儲存的迭代器失效。
*/
    std::vector<int> numbers;

    numbers.push_back(42);
    numbers.push_back(314159); 

    for (int i : numbers) { // c++11 range-based for loop
        std::cout << i << '\n';
    } 

    return 0;
}

輸出:

42
314159
  • pop_back:
#include <vector>
#include <iostream>

int main()
{
    std::vector<int> numbers = {45,46};

    numbers.pop_back();
    for (int i : numbers) { // c++11 range-based for loop
        std::cout << i << '\n';
    }
    system("pause");
    return 0;
}

輸出:

45
  • emplace_back():
#include <vector>
#include <iostream>

struct Test {
    int a;
    Test(int a1) {
        a = a1;
    }
};

int main()
{
    //emplace_back是c++ 11新定義的一種用於替代push_back
    //因為vector很多時候,需要我們儲存一些類或者帶建構函式的結構
    //那麼如果是使用push_back的話,我們需要新生成一個臨時的物件,然後
    //在拷貝到vector中,但是emplace_back不用,它可以直接呼叫建構函式,
    //新建一個物件,儲存到vector中
    std::vector<Test> numbers;

    numbers.emplace_back(45);
    numbers.emplace_back(4);
    //numbers.push_back(Test(5)); //push_back的方式
    for (Test i : numbers) { // c++11 range-based for loop
        std::cout << i.a << '\n';
    }
    system("pause");
    return 0;
}

輸出:

45
4
  • emplace():
#include <vector>
#include <iostream>

struct Test {
    int a;
    Test(int a1) {
        a = a1;
    }
};

int main()
{
    //emplace是c++ 11新定義的一種函式,它的使用方法就好比push_back對應insert,
    //emplace_back對應emplace

    std::vector<Test> numbers;

    numbers.emplace(numbers.cbegin(), 45);

    for (Test i : numbers) { // c++11 range-based for loop
        std::cout << i.a << '\n';
    }
    system("pause");
    return 0;
}

輸出:

45
  • insert():
#include <vector>
#include <iostream>

struct Test {
    int a;
    Test(int a1) {
        a = a1;
    }
};

int main()
{
    /*
    insert有很多版本,下面只列舉其中一種
    iterator insert( iterator pos, const T& value );
    iterator insert( const_iterator pos, const T& value );
    iterator insert( const_iterator pos, T&& value );
    void insert( iterator pos, size_type count, const T& value );
    iterator insert( const_iterator pos, size_type count, const T& value );
    template< class InputIterator >
    void insert( iterator pos, InputIterator first, InputIterator last);
    template< class InputIterator >
    iterator insert( const_iterator pos, InputIterator first, InputIterator last );
    iterator insert( const_iterator pos, std::initializer_list<T> ilist );
    */
    std::vector<Test> numbers;
    numbers.insert(numbers.cbegin(), Test(4));

    for (Test i : numbers) { // c++11 range-based for loop
        std::cout << i.a << '\n';
    }
    system("pause");
    return 0;
}

輸出:

4
  • erase()
#include <vector>
#include <iostream>


int main( )
{
/*
iterator erase( iterator pos );
iterator erase( const_iterator pos );

iterator erase( iterator first, iterator last );
iterator erase( const_iterator first, const_iterator last );
*/
//初始化vector的另外一種方式,
    std::vector<int> c{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    for (auto &i : c) {
        std::cout << i << " ";
    }
    std::cout << '\n';

    c.erase(c.begin());

    for (auto &i : c) {
        std::cout << i << " ";
    }
    std::cout << '\n';

    c.erase(c.begin()+2, c.begin()+5);

    for (auto &i : c) {
        std::cout << i << " ";
    }
    std::cout << '\n';
}

輸出:

0 1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9
1 2 6 7 8 9

8、vector用於操作大小的函式


 1. c.size()   返回當前的元素數量
 2. c.empty()  判斷大小是否為0
 3. c.max_size() 返回可容納的元素的最大的數量
 4. c.capacity()   返回重新分配空間前,所能容吶的元素的最大量
 5. c.reserve()    重新分配vector的最大容量
 6. c.shrink_to_fit()  (c++ 11新標準),就是把vector中沒有使用的空間給釋放了

函式的使用示例:

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


int main()
{
    vector<int> test = { 1,2,3,4,5,6 };
    cout << "size()=" << test.size() << endl;
    cout << "max_size()=" << test.max_size() << endl;
    cout << "capacity()=" << test.capacity() << endl;
    //重新分配最大的容量
    test.reserve(100);
    cout << "重新分配後的";
    cout << "max_size()=" << test.max_size() << endl;
    cout << "重新分配後的";
    cout << "capacity=" << test.capacity() << endl;

    test.shrink_to_fit(); //清除無用的記憶體
    cout << "清除無用的記憶體";
    cout << "capacity=" << test.capacity() << endl;

    system("pause");
    return 0;
}

輸出如下:
這裡寫圖片描述

現在我們可以補充一個知識:

vector 存在預分配機制。可以在元素不存在的情況下預分配一段空間,為以後的儲存做準備。這段空間可以用reserve()調節。capacity()返回的值就是可以存放元素的個數。capacity() - size()就是下次重新進行空間分配前的預留元素個數。至於max_size()指的是一個vector結構可供儲存元素的個數的上線,通常是由於定址空間決定的。

所以reserve是調節capacity的值,而不是max_size的值,而且reserve會造成vector指標、迭代器失效。

測試程式碼

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


int main()
{
    vector<int> v(4);
    cout << v.capacity() << endl;
    cout << v.size() << endl;

    std::vector<int>::iterator test = v.begin();
    cout << *test << endl;
    v.push_back(1);
    //因為這裡它進行了記憶體的重新分配,所以會造成迭代器失效,下面這個程式碼是
    //有錯誤的,但是編譯不會出錯,只是到時候我們發現解引用時
    //解的是一個未被分配的記憶體地址
    cout << *test << endl;
    system("pause");
    return 0;
}

輸出:

4
4
0
(到這裡,程式會出現異常)

9、vector的賦值


 1. c1=c2   這個之前已經介紹了
 2. c.assign (n,elem)  複製n個elem
 3. c.assign(beg,end)  將區間[beg,end]內的元素賦值給c,可以進行不同型別的容器但是容器存放的同種型別的元素的容器複製
 4. c.swap(c2)   將c1和c2元素互換,
 5. swap(c1,c2)  同上,但是它是全域性函式,

下面是對各個函式進行例項展示:

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


int main()
{
    vector<int> v1 = { 1,2,3,4,5 };
    cout << "v1為:" << endl;

    for (auto test : v1) {
        cout << test << " ";
    }
    cout << endl;
    vector<int> v2;
    v2.assign(4, 1);
    cout << "v2為:" << endl;
    for (auto test : v2) {
        cout << test << " ";
    }
    cout << endl;
    //進行不同容器之間的元素複製
    vector<char> v3;
    string str = "new";
    v3.assign(str.cbegin(), str.cend());
    cout << "v3為:" << endl;
    for (auto test : v3) {
        cout << test << " ";
    }
    cout << endl;

    cout << "交回v1和v2" << endl;
    v1.swap(v2);
    cout << "v1為:" << endl;

    for (auto test : v1) {
        cout << test << " ";
    }
    cout << endl;
    cout << "v2為:" << endl;
    for (auto test : v2) {
        cout << test << " ";
    }
    cout << endl;
    system("pause");
    return 0;
}

輸出:
這裡寫圖片描述

記住兩個vector交換後,他們的迭代器不會失效,只是分別屬於另外一個的容器的迭代器。