返回顶部

【STL基础】序列式容器之vector

参照自文档http://www.cplusplus.com/reference/array/,教程http://c.biancheng.net/view/6688.html,和书籍《STL源码剖析》(侯捷)

定义:

template < class T, class Alloc = allocator<T> > class vector;

vector与array非常类似,但是其对于空间的操作更为灵活,vector使用动态空间。

动态增加空间并不是直接在原空间后接续新的空间,而是以原空间大小的两倍配置一块较大的空间,然后进行原数据的拷贝。

使用需要引入头文件和使用std命名空间。

(一)初始化

方式一:创建了一个空的 vector 容器,并未分配空间。

std::vector<double> values;

为空容器分配20个容量的内存。注意,如果 vector 的容量在执行此语句之前,已经大于或等于 20 个元素,那么这条语句什么也不做。

values.reserve(20);

方式二:创建并初始化。

std::vector<double> values {2, 3, 5, 7, 11};

方式三:创建并指定元素个数,此时这二十个元素都被初始化为0.0。

std::vector<double> values(20);

方式四:创建并指定元素个数和初始化值,此时这二十个元素都被初始化为1.0。

std::vector<double> values(20, 1.0);

方式五:直接拷贝其他的vector。

std::vector<char> value1(5, 'c');
std::vector<char> value2(value1);

也可以选择性复制一部分元素(利用迭代器函数)。

std::vector<int> value1 {1, 2, 3, 4, 5};
std::vector<int> value2(std::begin(value1), std::begin(value1 + 3);//将保存1,2,3

(二)迭代器函数

  • begin():返回指向容器中第一个元素的随机访问迭代器。
  • end():返回指向容器最后一个元素后一个位置的随机访问迭代器。
  • rbegin():返回指向最后一个元素的随机访问迭代器
  • rend():返回指向第一个元素前一个位置的随机访问迭代器
  • cbegin():和 begin() 功能相同,只不过在其基础上增加了 const 属性,不能用于修改元素。
  • cend()、crbegin()、crend():依此类推

    前缀带c的返回的迭代器是const类型,因此只能通过它读取而不能修改元素。另外,空的vector不能使用迭代器。如果vector申请了更多内存,比如使用reserve()函数,可能会导致之前的迭代器失效,因为其地址可能改变了。每当 vector 容器的容量发生变化时,我们都要对之前创建的迭代器重新初始化一遍。

(三) 容量函数

  • size():返回容器中当前元素的数量,其值始终等于初始化 array 类的第二个模板参数 N。
  • max_size():返回元素个数的最大值。这通常是一个很大的值,一般是 2^32-1,所以我们很少会用到这个函数。
  • resize():改变实际元素的个数。
  • capacity():返回当前容量。
  • empty():判断容器是否为空。
  • reserve():增加容器的容量。
  • shrink_to_fit():将内存减少到等于当前元素实际所使用的大小。

(四)元素访问函数

  • at(n):使用经过边界检查的索引访问元素,获取指定位置的元素引用。当传给 at() 的索引会造成越界时,会抛出std::out_of_range异常。
  • front():返回容器中第一个元素的直接引用,可以用于修改元素,但是也只能修改第一个元素(因为它是引用。。。)。
  • back():返回容器中最后一个元素的直接引用,可以用于修改元素。
  • data():返回一个指向容器首个元素的指针,可以用于修改元素。

(五)修改器函数

  • assign():用新元素替换原有内容。
  • push_back():在序列的尾部添加一个元素。
  • pop_back():移出序列尾部的元素。该容器的大小(size)会减 1,但容量(capacity)不会发生改变。
  • insert():在指定的位置插入一个或多个元素,具体用法如下:
    • iterator insert(pos, elem):在迭代器 pos 指定的位置之前插入一个新元素elem,并返回表示新插入元素位置的迭代器。
    • iterator insert(pos, n, elem): 在迭代器 pos 指定的位置之前插入 n 个元素 elem,并返回表示第一个新插入元素位置的迭代器。
    • iterator insert(pos, first, last):在迭代器 pos 指定的位置之前,插入其他容器(不仅限于vector)中位于 [first,last) 区域的所有元素,并返回表示第一个新插入元素位置的迭代器。
    • iterator insert(pos, initlist):在迭代器 pos 指定的位置之前,插入初始化列表(用大括号{}括起来的多个元素,中间有逗号隔开)中所有的元素,并返回表示第一个新插入元素位置的迭代器。
//insert()源码
//从position开始,插入n个初值为x的元素
template <class T, class Alloc>
void insert(iterator position, size_type n, const T& x){
    if(n != 0){
        if(size_type(end_of_storage - finish) >= n){
            //剩余空间大于n时
            T x_copy = x;
            //计算插入后现有元素的个数
            const szie_type elems_after = finish - position;
            iterator old_finish = finish;
            if(elems_after > n){
                //插入点之后的现有元素个数大于新增元素个数
                uninitialized_copy(finish - n, finish, finish);//先后移原vector中插入点之后的元素
                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);//先在原vector的末尾填几个新值
                finish += n - elems_after;
                uninitialized_copy(position, old_finish, finish);//然后将插入点后的元素拷贝到上面插入新值之后的位置
                finish += elems_after;
                fill(position, old_finish, x_copy);//最后将前面空出来的n - elems_after个空位用新值填满
            }
        }
        else{
            //剩余空间小于n,必须配置额外的内存
            //首先决定长度,旧长度的两倍,或者旧长度+新增元素的个数(取最大值)
            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);
                //将新增元素复制到新空间
                new_finish = uninitialized_fill_n(new_finish, n, x);
                //将旧vector的插入点之后的元素复制到新空间
                new_finish = uninitialized_copy(position, finish, new_finish);
            }
            # ifdef __STL_USE_EXCEPTIONS
            catch(...) {
                //异常发生则回收空间
                destroy(new_start, new_finish);
                data_allocator::deallocate(new_start, len);
                throw;
            }
            # endif
            //清除并释放旧的vector
            destroy(start, finish);
            deallocate();
            //调整水位标记
            start = new_start;
            finish = new_finish;
            end_of_storage = new_start + len;
        }
    }
}
//使用示例
std::vector<int> demo{1,2};
//第一种格式用法
demo.insert(demo.begin() + 1, 3);//{1,3,2}
//第二种格式用法
demo.insert(demo.end(), 2, 5);//{1,3,2,5,5}
//第三种格式用法
std::array<int,3> test{7,8,9};
demo.insert(demo.end(), test.begin(), test.end());//{1,3,2,5,5,7,8,9}
//第四种格式用法
demo.insert(demo.end(), {10,11});//{1,3,2,5,5,7,8,9,10,11}
for (int i = 0; i < demo.size(); i++) {
    cout << demo[i] << " ";
}
  • erase():删除 vector 容器中 pos 迭代器指定位置处的元素,并返回指向被删除元素下一个位置元素的迭代器。该容器的大小(size)会减 1,但容量(capacity)不会发生改变。
//erase()源码
//1.清除[first, last)间的所有元素
iterator erase(iterator first, iterator last){
    iterator it = copy(last, finish, first);//copy是全局函数
    destroy(it, finish);//destroy是全局函数
    finish = finish - (last - first);
    return first;
}
//2.清除position位置的元素
iterator erase(iterator position){
    if(position + 1 != end())
        copy(position + 1, finish, position);//copy是全局函数
    --finish;
    destroy(finish);//destroy是全局函数
    return position;
}
//使用示例
vector<int>demo1{ 1,2,3,4,5 };
auto iter = demo1.erase(demo1.begin() + 1);//删除元素 2
cout << endl << *iter << endl;//iter迭代器指向元素 3
vector<int>demo2{ 1,2,3,4,5 };
iter = demo2.erase(demo2.begin()+1, demo2.begin()+3);//删除元素2和3
  • swap():交换两个容器的所有元素。
  • clear():移除所有的元素,容器大小变为 0。
//clear()源码
void clear(){
		erase(begin(), end());
}
  • emplace():在指定位置之前生成一个新的元素。(与insert()区分开来)
  • emplace_back():在序列尾部生成一个元素。

emplace_back()和push_back()的区别,在于底层实现的机制不同。push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。

push_back() 的底层实现过程比 emplace_back() 更繁琐,换句话说,emplace_back() 的执行效率比 push_back() 高。因此,在实际使用时,建议大家优先选用 emplace_back()。

(六)重载运算符

  • vector可以通过运算符[]以下标的形式访问元素。

(七)示例

#include <iostream>
#include <vector>

using namespace std;

int main(){
    //初始化一个空的vector
		vector<char> value;
    //在尾部添加若干元素
    value.push_back('S');
    value.push_back('T');
    value.push_back('L');
    //打印出vector的尺寸
    printf("元素个数为:%d\n", value.size());
    //遍历vector(使用迭代器)
    for(auto it = value.begin(); it < value.end(); ++it){
        cout << *it << " ";
    }
    cout << endl;
    //在首部插入一个元素
    value.insert(value.begin(), 'C');
    cout << value.at(0) << endl;

    vector<int> values{1,2,3,4,5};    
    //输出容器中第 3 个元素的值
    cout << *(values.data() + 2) << endl;
    //修改容器中第 2 个元素的值
    *(values.data() + 1) = 10;
    cout << *(values.data() + 1) << endl;
    return 0;
}
posted @ 2020-11-12 20:55  藤原豆腐店の張さん  阅读(142)  评论(0编辑  收藏  举报