淺讀《STL源碼剖析》筆記 4章-vector&&list

個人博客傳送門

4 序列式容器

4.1 容器的概觀與分類

SGI STL的各個容器
所謂序列式容器,其中的元素都是可序的(ordered),但未必有序(sorted)。C++本身有array,其它是STL提供的。

4.2 vector

4.2.1 vector概述

array是靜態的,vector是動態增長的,隨着元素的增加,內部機制自動擴充空間以容納元素,不需要自己分配空間。

4.2.3 vector的迭代器

由於vector維護的是連續線性空間,所以不論元素類型是什麼,普通指針都可以作爲vector的迭代器而滿足要求。vector支持隨機存取,普通指針也滿足。vector<int> :: iterator ivite;vector<Shape> :: iterator svite;其中 ivite的類型就是int*svite的類型就是Shape*

4.2.4 vector的數據結構

vector的數據結構如下:

template<class T, class Alloc = alloc>
class vecotr{
    //...
    protected:
    //注意STL的左閉右開特性。finish和end_of_storage指向最後一個元素的下一個位置
        iterator start;             //表示目前使用空間的頭部
        iterator finish;            //表示目前使用空間的尾部
        iterator end_of_storage;    //表示目前可用空間的尾部
}

爲了降低空間配置時的成本,實際上,vector配置的空間會比客戶端需要的更大一些,這樣是爲了將來可能擴充的準備。當容量等於大小的時候,開闢新的空間。運用 start,finish,end_of_storage三個迭代器,可以實現begin(),end(),size(),capacity(),empty(),operator[],front(),back()等方法。

4.2.5 vector的構造與內存管理:constructor,push_back

push_back:將新元素插入vector尾端的時候,先檢查是否還有備用空間,夠的話,直接構造元素,並調整finish。如果沒有備用空間,動態增長空間。這指並不是在原空間之後開闢新的空間,因爲無法保證原空間之後還有可供配置的空間。而是以原大小的兩倍例外配置一塊大的空間,然後將原內容拷貝過來,然後纔開始在原內容之後構造新元素,並釋放原空間。因此,對於vector的任何操作,一旦引起了空間重新配置,指向原vector的所有迭代器都是失效了。 push_back源代碼節選如下:

void push_back( const T& x){
    if( finish != end_of_storage ){
        construct( finish, x );
        ++finish;
    }
    else    //無備用空間
    insert_aux(end(), x);
}

template <class T, class Alloc>
void vector<T, Alloc>::insert_aux( iterator positon, 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;
        iterator new_start = data_allocator::allocatr(len); //實際配置空間
        iterator new_finish = new_start;
        try{
            //將原來vector內容拷貝到新的vector
            new_finish = uninitialized_copy(start, position, new_start);
            //爲新元素設定初值x
            construct(new_finish, x);
            ++new_finish;
            //將安插點的原內容也拷貝過來//不懂
            new_finish = uninitialized_copy(posiition, finish, new_finish);
        }
        catch(...){
            //開闢失敗
            destroy(new_start, new_finish);
            data_allocator::deallocate(new_start, len);
            throw;
        }
        //析構並釋放原vector
        destory(begin(), end());
        deallocate();
        //調整迭代器,指向新的vector
        start = new_start;
        finish = new_finish;
        end_of_storage = new_start+len;
    }
}

4.2.6 vector的元素操作:pop_back, erase, clear, insert

//清除[first, last)中的元素
iterator erase(iterator first, iterator last){
    iterator ii = copy(last, finish, first);    //copy是全局函數,第六章
    destory(i, finish);
    finish = finish - (last - first);
    return first;
}
//清除某個位置上的元素
iterator erase(iterator position){
    if(position + 1 != end())
        copy(position + 1, finish, position);
    --finish;
    destroy(finish);
    return position;
}

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

//從position開始,插入n個元素,元素初值爲x
template <class T, class Alloc>
void vector<T, Alloc>::insert(iterator position, size_type n ,const T& x){
    if(n != 0){
        //備用空間大於等於新增元素個數
        if(size_type(end_of_storage - finish) >= 0){
            T x_copy = x;
            //計算插入點之後的現有元素個數
            const size_type elems_after = finish - position;
            iterator old_finish = finish;
            if(elems_after > n){    //插入點之後的現有元素個數 > 新增元素個數
                uninitialized_copy(finish - n, finish, finish);
                finish += n;    //將vector 尾端標記後移
                copy_backward(position, old_finish - n, old_finish);
                fill(position, position+n, x_copy); //從插入點開始填入新值
            }
            else{
                uninitialized_fill_n(finish, n-elems_affter, x_copy);
                finish += n - elems_after;
                uninitialized_copy(position, old_finish, finish);
                finish += elems_after;
                fill(position, old_finish, x_copy);
            }
        }
        else{   //備用空間 < 新增元素個數
            const size_type old_size = size();
            //決定新的長度爲舊長度+新增元素個數
            const size_type len = old_size + max(old_size, n);
            //配置新的vector空間
            iterator new_start = data_allocaator::allocate(len);
            iterator new_finish = new_start;
            __STL_TRY{  //<-- 這個是什麼
                //將舊的vector在插入點之前的元素複製到新空間
                new_finish = uninitialized_copy(start, position, new_start);
                //將新增元素(初值爲x)填入新空間
                new_finish = uninitialized_fill_n(new_finish, n, x);
                //將舊的vector在插入點之後的元素複製到新空間
                nwe_finish = uninitialized_copy(position, finish, new_finish);
            }
            //異常處理
            //...

            //清除釋放舊的空間
            destroy(start,finish);
            deallocate();
            //調整迭代器指向新的空間
            start = new_start;
            finish = new_finish;
            end_of_storage = new_start+len;
        }
    }
}
//插入操作完成之後,新增節點應位於position的後面。

圖解如下:
insert

4.3 list

4.3.1 list概述

list每次插入或刪除一個元素,就配置或釋放一個元素空間。對於任何位置的元素插入或元素移除,是時間常數。

4.3.2 list的節點(node)

list的節點和list本身的設計是分開的。以下是STL list的節點結構:

template <class T>
struct __list_node{
    typedef void* void_pointer;
    void_pointer prev;  //型別爲void*,其實可以是__list_node<T>
    void_pointer next;
    T data;
}
//這是一個雙向鏈表節點

4.3.3 list的迭代器

list的迭代器有一個重要性質,insert(插入)和splice(接合)操作都不會造成原有的迭代器失效。list的delete(刪除)只有被刪除的那個節點的迭代器失效

4.3.4 list的數據結構

SGI list 是一個雙向循環鏈表。list結構如下:

template <class T, class Alloc = alloc>
class list{
protected:
    typedef __list_node<T> list_node;
public:
    typedef list_node* link_type;
protected:
    link_type node;
}

STL中指針node指向尾端的一個空白節點,以符合STL中前閉後開規範。

iterator begin() { return (link_type)((*node).next); }
iterator end() { return node; }
bool empty() const { return node->next == node; }
size_type size() const {
    size_type result = 0;
    distance(begin(), end(), result);   //全局函數,第三章//計算兩個迭代器之間的距離
    return result;
}
reference front() { return *begin(); }
reference back() { return *(--end()); }

圖解如下:
list

4.3.5 list的構造與內存管理:constructor, push_back, insert

list缺省使用alloc,並由此定義了一個list_node_alloctor是爲了更方便的以節點大小爲配置單位。list_node_alloctor(n)表示配置n個節點空間。同時有四個函數,如下:

//配置一個節點並傳回
link_type get_node();
//釋放一個節點
void put_node(link_type p);
//配置並構造一個節點,帶有元素值
link_type create_node(const T& x);
//析構並釋放一個節點
void destroy_node(link_type p);

list衆多構造函數中,有一個允許我們構造一個空list出來:

public:
    list(){ empty_initialize(); }
protected:
    void empty_initialize(){
        //next、prev指針都指向自己
        node = get_node();
        node->next = node;
        node->prev = node;
    }

空節點對象模型:
空節點
當我們用push_back()插入新節點的時候,函數內部調用insert()void push_back(const T& x) { inset( end(), x ); }insert()有很多的重載函數,最簡單的如下:

//在迭代器position所指位置插入一個節點,值爲x
iterator insert(iterator position, const T& x){
    link_type tmp = create_node(x);
    //插入位置在position之前,這是STL規範。
    tmp->next = position.node;
    tmp->prev = position.node->prev;
    (link_type(position.node->node->prev))->next = tmp;
    position.node->prev = tmp;
    return tmp;
}

4.3.6 list的元素操作:push_front, push_back, erase, pop_front, pop_back, clear, remove, unique, splice, merge, reverse, sort

push_front, push_back複用insert;pop_front, pop_back複用erase。

//移除迭代器position所指節點
iterator erase(iterator position){
    link_type next_node = link_type(position.node->next);
    link_type prev_node = link_type(position.node->prev);
    prev_node->next = next_node;
    next_node->prev = prev_node;
    destroy_node(position.node);
    return iterator(next_node);
}
//清除所有節點
template <class T, class Alloc>
void list<T, Alloc>::clear(){
    link_type cur = (link__type) node->next;    //begin();
    while( cur != node ){
        link_type tmp - cur;
        cur = (link_type)cur->next;
        destroy_node(tmp);
    }
    //恢復成空節點的初始結構
    node->next = node;
    node->prev = node;
}
//將數值爲value的所有元素移除
template <class T, class Alloc>
void list<T, Alloc>::remove(const T& value){
    iterator first = begin();
    iterator last = end();
    while(first != last){
        iterator next = first;
        ++next;
        if(*first == value) erase(first);
        first = next;
    }
}
//移除相同連續的元素,只有連續相同的元素纔會被移除只剩一個
//很帥啊
template <class T, class Alloc>
void list<T, Alloc>::unique(){
    iterator first = begin();
    iterator last = end();
    if(first == last) return;   //判空
    iterator next = first;
    while(++next != last){
        if(*first == *next)
            erase(next);
        else
            first = next;
        next = first;
    }
}

list 內部提供了一個遷移操作(transfer):將某連續分爲的元素遷移到某特定位置之前。

protected:
    //將[first, last)內的所有元素移動到position之前。
    void transfer(iterator position, iterator first, iterator last){
        if(position != last){
            //先處理各節點的next
            (*(link_type((*last.node).prev))).next = position.node;
            (*(link_type((*first.node).prev))).next = last.node;
            (*(link_type((*position.node).prev))).next = first.node;
            //tmp爲position的prev節點
            link_type tmp = link_type((*position.node).prev);
            //處理各節點的prev
            (*position.node).prev = (*last.node).prev;
            (*last.node).prev = (*first.node).prev;
            (*first.node).prev = tmp;
        }
    }

splice各個版本:

public:
    //將list x接合與position所指位置之前,x必須不同於*this
    void splice(iterator position, list& x){
        if(!x.empty())
            transfer(position, x.begin(), x.end());
    }
    //將i 所指元素接合於position所指元素之前。position和i可指向同一個list
    void splice(iterator position, list&, iterator i){
        iterator j = i;
        ++j;
        if(position == i || position == j) return;
        trasfer(position, i, j);
    }
    //將[first, last)內的所有元素接合於position所指位置之前,
    //position和[first, last)可指向同一個list。
    //但是position不能在[first, last)範圍之內
    void splice(iterator posiition, list&, iterator first, iterator last){
        if(first != last)
            transfer(position, first, last);
    }

merge(), reverse(), sort()源碼:

//merge()將x合併到*this上,兩個list必須是遞增排序的
template <class T, class Alloc>
void list<T, Alloc>::merge(list<T, Alloc>& x){
    iterator first1 = begin();
    iterator last1 = end();
    iterator first2 = x.begin();
    iterator last2 = x.end();
    while(first1 != last1 && first2 != last2){
        if(*first2 < *first1){
            iterator next = first2;
            transfer(first1, first2, ++next);
            first2 = next;
        }
        else
            ++first1;
        if(first2 !=  last2) transfer(last1, first2,last2);
    }
}

//reverse()將*this的內容逆置
template <class T, class Alloc>
void list<T, Alloc>::reverse(){
    //判空或只有一個節點,用size() == 0 || size() == 1速度比較慢
    if(node->next == node || link_type(node->next)->next == node)
        return;
    iterator first = begin();
    ++first;
    while(first != end()){
        iterator old = first;
        ++first;
        transfer(begin(), old, first);
    }
}

//list不能使用STL中的sort()算法,只能使用自己的sort()
//因爲STL的sort()只接受RamdonAccessIterator
//本函數使用quick sort
template <class T, class Alloc>
void list<T, Alloc>::sort(){
     //判空或只有一個節點,用size() == 0 || size() == 1速度比較慢
    if(node->next == node || link_type(node->next)->next == node)
        return;
    //創建新的list空間,作爲中介數據存放區
    list<T, Alloc> carry;
    list<T, Alloc> counter[64];
    int fill = 0;
    while(!empty()){
        carry.splice(carry.begin(), *this, begin());
        int i = 0;
        while(i < fill && !counter[i].empty()){
            counter[i].merge(carry);
            carry.swap(counter[i++]);
        }
        carry.swap(counter[i]);
        if(i == fill)
            ++fill;
    }
    for(int i = 1; i < fill; ++i)
        counter[i].merge(counter[i-1]);
    swap(counter[fill-1]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章