STL—vector

前面介紹了STL對象的構造與析構以及內存的配置與釋放,那具體的容器是怎麼應用STL的空間配置器的呢?這篇先介紹STL的容器vector。html

 

vector的數據成員

vector只有4個數據成員:3個迭代器、1個內存配置器。git

STL會爲每一個容器都設置一個內存配置器的成員,這裏的內存配置器就是前面介紹的STL空間配置器,使用了統一對外接口的類simple_alloc,即STL會爲每一個容器都定義一個simple_alloc類的類型成員,經過該類型成員來爲容器分配內存。github

vector的迭代器就是原始指針,只不過用了typedef將迭代器的類型變爲了iterator,其實它就是T* 。vector的3個迭代器分別指向當前內存的起始地址(start)、最後一個數據的尾後地址(finish)、整個內存的最後地址(end_of_storage)。源碼以下:ide

class vector
{
public:
        typedef T                       value_type;
        typedef value_type*             pointer;
        typedef const value_type*       const_pointer;
        typedef value_type*             iterator;//vector迭代器就是一個原生指針
        typedef const value_type*       const_iterator;
        typedef value_type&             reference;
        typedef const value_type&       const_reference;
        typedef size_t                  size_type;
        typedef ptrdiff_t               difference_type;
        typedef MiniSTL::reverse_iterator<iterator>             reverse_iterator;
        typedef MiniSTL::reverse_iterator<const_iterator>       const_reverse_iterator;
        typedef alloc allocator_type;

        allocator_type get_allocator() const { return allocator_type(); }


private:
        typedef simple_alloc<T, allocator_type> data_allocator;
        iterator        start;
        iterator        finish;
        iterator        end_of_storage;

 

vector對象的構造

vector有多種構造函數,但作的事情都同樣,即先調用內存配置器去分配一塊內存,而後對這塊內存初始化,最後設置3個迭代器成員,讓它們指向正確的位置。函數

以咱們日常使用最多的vector構造方法,如:vector<int> vec(10); 爲例,其對應的構造函數以下,下面還將該構造過程涉及到的函數一併列出:源碼分析

構造函數vector(size_type n)調用fill_initializer函數,該函數會調用allocate_and_fill函數先去分配一塊內存,而後進行初始化,因爲這種構造方式未提供初始值,則按T類型的默認初始化進行初始化,而後剩下工做就是設置好start、finish、end_of_storage迭代器,工做便完成。spa

        void fill_initializer(size_type n, const T& value)
        {
                start = allocate_and_fill(n, value);
                finish = start + n;
                end_of_storage = finish;
        }

        iterator allocate_and_fill(size_type n, const T& value)
        {
                iterator result = data_allocator::allocate(n);
                uninitialized_fill_n(result, n, value);
                return result;
        }

public:
        vector() : start(0), finish(0), end_of_storage(0) {}
        explicit vector(size_type n) { fill_initializer(n, T()); }

 

vector空間的動態增加

當咱們向vector進行push_back時,若原內存空間未滿,那很好,直接在後面添加一個元素便可。若原內存空間已滿,則不能直接在其後面添加了,由於誰也不知道原空間後面的內存究竟是魔鬼仍是天使。指針

因此,若原空間內存已滿,繼續往vector添加元素時,會先調用內存配置數據成員,分配一塊新的內存,爲了減小內存分配的次數,因此既然要分配了,那乾脆就多分點,因此這塊新內存的大小爲原空間內存的2倍。code

接着,將原內存上的數據拷貝到新內存中--->析構原內存空間中的對象--->釋放原內存空間--->從新設置迭代器。htm

vector添加元素時,會致使內存空間的從新分配,因此會致使以前的迭代器都失效。

vector要是常常這樣動態增加會致使程序效率降低,因此能夠調用vector的reserve函數預先分配一大塊指定大小的內存,以減小內存重分配次數。

對照下面源碼分析。當finish和end_of_storage相等,則知道已經沒有剩餘空間了,push_back會調用insert_aux函數完成剩下所有工做。insert_aux函數調用allocate函數分配原空間2倍大小的新內存空間,調用uninitialized_copy函數將原內存中數據拷貝到新內存,接着調用destroy析構原空間中對象,調用deallocate()釋放原內存空間,並從新設置start、finish、end_of_storage迭代器。以下:

push_back : 

        void push_back(const T& val)
        {
                if (finish != end_of_storage)
                {
                        construct(finish, val);
                        ++finish;
                }
                else
                        insert_aux(end(), val);
        }

insert_aux : 

template<typename T>
void
vector<T>::insert_aux(iterator position, const T& x)
{
        if (finish != end_of_storage)  //還有備用空間
        {
                construct(finish, *(finish - 1));
                ++finish;
                T x_copy = x;
                copy_backward(position, finish - 2, finish - 1);
                *position = x_copy;
        }
        else
        {
                const size_type old_size = size();
                const size_type len = old_size != 0 ? 2 * old_size : 1;  //2倍原空間大小
                iterator new_start = data_allocator::allocate(len);
                iterator new_finish = new_start;

                try {
                        new_finish = uninitialized_copy(start, position, new_start);
                        construct(new_finish, x);
                        ++new_finish;
                        new_finish = uninitialized_copy(position, finish, new_finish);
                }
                catch(...) {
                        destroy(new_start, new_finish);
                        data_allocator::deallocate(new_start, len);
                        throw;
                }

                destroy(begin(), end()); // 析構原內存空間的對象
                deallocate(start, end_of_storage - start); // 釋放原內存空間
                start = new_start;
                finish = new_finish;
                end_of_storage = new_start + len;
        }
}
View Code

 

有關vector空間的動態增加的詳細介紹可參考個人另外一篇文章:http://www.cnblogs.com/zxiner/p/7197327.html

 

(全文完)

附:
一款簡易版STL的實現,項目地址: https://github.com/zinx2016/MiniSTL/tree/master/MiniSTL
相關文章
相關標籤/搜索