STL vector简单理解

a 夏天 提交于 2019-12-19 09:40:10

最近在知乎上看到一个回答:

https://www.zhihu.com/question/49995099

怎样读《二十四史》。重点在于苏轼有个读书的方法,“每书数过,一意求之”。读书要带着目的读,有所取舍,反复多次,才得到的多。

我一想,写代码看这些技术书也是啊。一次只看一个侧重点,反复多次。而不是每次都从头看,这样效率低下,学不到东西。

 

工作中经常使用vector,具体细节并未了解。通常情况下,如果长度固定的数据结构会使用数组,长度不定则使用vector。

最近又翻看了《STL源码剖析》,发现以前看不懂的地方可以看懂了。这次看源码剖析,就看vector。

 

首先需要有一个整体的概念。所谓vector,是一个动态的数组。使用的时候不需要像数组一样关心长度(但是同样有越界)。

使用[]操作符,同样需要考虑越界。不需要关心长度指的是往里面增加元素时,push_back时vector会动态增加。

 

数组和vector都是连续的内存空间。区别在于数组配置了就不能改变,需要变大,需要另外申请空间,把数据从原来的地方搬到新的地方。

这些工作需要用户(程序员)自己做。而vector不需要,它封装了这些操作。vector有很多实现版本。核心难点在于空间满载时,扩充空间时的三个操作,

申请新空间--数据迁移--释放旧空间

 

简单看一下vector有哪些内容:

// simple vector
template <class T, class Alloc = alloc>
class YVector
{
public:
    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;

    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() { return begin() == end(); }
    reference operator[](size_type n) { return *(begin() + n); }

    reference front() { return *begin(); }
    reference back() { return *(end() - 1); }
    void push_back(const T& x);
    void pop_back();
    iterator erase(iterator first, iterator last);
    iterator erase(iterator position);
    void resize(size_type n, const T& x);
    void resize(size_type n);
    void clear() { erase(begin(), end());  }

    YVector() :start(0), finish(0), end_of_storage(0) {}
    YVector(size_type n, const T& value) { fill_initialize(n, value);  }
    YVector(int n, const T& value) { fill_initialize(n, value); }
    YVector(long n, const T& value) { fill_initialize(n, value); }
    explicit YVector(size_type n) { fill_initialize(n, T());  }

    ~YVector()
    {
        destroy(start, finish);
        deallocate();
    }

protected:
    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();
    void fill_initialize(size_type n, const T& value);
    void allocate_and_fill(size_type n, const T& value);
};

看到有很多关于alloc的,这其实是内存分配的问题,在源码剖析里第二章专门讲了这部分。由于目前阶段只需要会用vector就行了,不需要理解到太深。

所有我想关于内存的地方,不深究实现代码,只大概明白做了什么就行。接下来就来具体看几个常用的,而且能看懂的部分。

 

1.iterator

typedef value_type* iterator;

vector的迭代器是一个简单的指针。6哦朋友。迭代器是个复杂的东西。在源码剖析第三章详细介绍。

为什么vector的迭代器是个简单的指针,因为vector迭代器需要的操作*,->,++,--,+=,-=,+,-这些普通的指针都具备。

 

2.size和capacity

vector的大小和容量是怎么界定的。如图:

关注3个指针:

iterator start;
iterator finish;
iterator end_of_storage;

start是起始位置,finish是结束位置,end_of_storage是申请空间的结束位置。有了这3个指针之后,很好理解一些操作:

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() { return begin() == end(); }    //是否为空
reference operator[](size_type n) { return *(begin() + n); }    //[]操作
reference front() { return *begin(); }        //首元素
reference back() { return *(end() - 1); }    //尾元素

size()其实是finish和start之间的大小,capacity是end_of_storage到start之间的大小。

3.push_back

写一段代码测试:

#include <stdlib.h>
#include <iostream>
#include <vector>

using namespace std;

void print_vec(vector<int> &vec)
{
    cout << "elem : ";
    for (int i = 0; i < (int)vec.size(); ++i)
    {
        cout << vec[i] << " ";
    }
    cout << endl;
    cout << "size = " << vec.size() << endl;
    cout << "capacity = " << vec.capacity() << endl;
    cout << endl;
}

int main()
{
    vector<int> vec(2, 1);
    print_vec(vec);

    vec.push_back(2);
    print_vec(vec);

    vec.push_back(2);
    print_vec(vec);

    vec.push_back(3);
    print_vec(vec);

    vec.push_back(4);
    print_vec(vec);

    vec.push_back(5);
    print_vec(vec);

    system("pause");
    return 0;
}

按书上说的需要扩充vector大小时,是capacity变成2倍大小。但是VS里面并不是

而在ubuntu里面g++编译下:

试过之后发现g++才是扩充2倍。VS是特殊版本的STL吧。

template <class T, class Alloc = alloc>
void YVector::push_back(const T& x)
{
    if (finish != end_of_storage)
    {
        construct(finish, x);
        ++finish;
    }
    else
    {
        insert_aux(finish, x);
    }
}

有备用空间时,把最后一个元素构造成X,finsih指针调整。没有备用了,调用内部insert_aux函数。insert_aux暂时不深入了吧。有点复杂。


Tips1:全局constuct函数。

void construct( pointer p, const _Ty& value ) 
{
     new(static_cast<void*>(p)) _Ty(value);
}

这是Vs里的版本。注意到是使用的placement new。c++里有3种new
1.new operator。申请空间,调用构造函数。不能被重载。

2.operator new。只申请空间,不调用构造函数。可重载。

3.placement new。一个特殊的operator new,在一块已经分配好的内存上构造对象,返回这块内存的首地址。

因为finish到end_of_storage之间是早已经申请好的空间,所以使用placement new。

 

4.pop_back,erase,clear

template <class T, class Alloc = alloc>
void YVector::pop_back()
{
    --finish;
    destroy(finish);
}

template <class T, class Alloc = alloc>
iterator erase(iterator first, iterator last)
{
    iterator i = copy(last, finish, first);
    destroy(i, finish);
    finish = finish - (last - first);
    return first;
}

template <class T, class Alloc = alloc>
iterator erase(iterator position)
{
    if (position + 1 != end())
    {
        copy(position + 1, finish, position);
    }
    --finish;
    destroy(finish);
    return position;
}

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

destroy()和copy()都是内存操作相关。destroy()相对于construct()。
iterator copy(first, last, result),copy负责拷贝,即将迭代器区间[first,last)的元素复制到由复制目标result给定的区间[result,result+(last-first))中,返回一个已复制区间的最后一个位置。

erase前和erase后的对比,如图:

5.resize和reserve

源码剖析里,好像没有特别讲。但是常用,容易弄错。写一段代码来理解怎么用

void print_vec(vector<int> &vec)
{
    cout << "size = " << vec.size() << endl;
    cout << "capacity = " << vec.capacity() << endl;
    cout << endl;
}

int main()
{
    vector<int> vec;
    print_vec(vec);

    vec.resize(100);
    print_vec(vec);

    vec.reserve(200);
    print_vec(vec);

    system("pause");
    return 0;
}

可以看到,resize是改变size和capacity的大小,而reserve是改变capacity大小。

引用:

1.《STL源码剖析》

2. http://www.cnblogs.com/luxiaoxun/archive/2012/08/10/2631812.html

3. http://blog.csdn.net/jerryjbiao/article/details/7376088

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!