vector源码2(参考STL源码--侯捷)

vector源码1(参考STL源码--侯捷)

vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效

vector的构造和内存管理

  vector所采用的数据结构非常简单:线性连续空间,它是由两个迭代器start和finish分别指向配置得来的连续空间中目前已被使用的范围,并以迭代器end_of_storage指向整块连续空间(含备用空间)的尾端:

class vector  //详细源码可见

{

..........

protected:

typedef simple_alloc<value_type,Alloc> data_allocator;  //simple_alloc是SGI STL的空间配置器

iterator start;   //表示目前使用空间的头

iterator finish;   //表示目前使用空间的尾

iterator end_of_storage;   //表示目前可用空间的尾

..........

  为了降低空间速配成本,vector的实际配置大小是原来容器大小的2倍,见下图:

#include<bits/stdc++.h>
using namespace std;

int main(){
    vector<int> v(3,3);
    cout<<v.size()<<"  "<<v.capacity()<<endl; //3 3
    v.push_back(5);
    cout<<v.size()<<"  "<<v.capacity()<<endl; //4 6
    v.push_back(6);v.push_back(7);v.push_back(8);
    cout<<v.size()<<"  "<<v.capacity()<<endl; //7 12

    for(int i=0;i<v.size();i++){ //3 3 3 5 6 7 8
        cout<<v[i]<<‘ ‘;
    }
    cout<<endl;

    v.pop_back();v.pop_back();
    cout<<v.size()<<"  "<<v.capacity()<<endl; //5 12
    v.pop_back();
    cout<<v.size()<<"  "<<v.capacity()<<endl; //4 12

    vector<int>::iterator it=find(v.begin(),v.end(),5);
    if(it!=v.end())
        v.erase(it);
    cout<<v.size()<<"  "<<v.capacity()<<endl; //3 12

    it=find(v.begin(),v.end(),3);
    if(it!=v.end())
        v.insert(it,4,7);
    cout<<v.size()<<"  "<<v.capacity()<<endl; //7 12

    for(int i=0;i<v.size();i++){ //7 7 7 7 3 3 3
        cout<<v[i]<<‘ ‘;
    }
    cout<<endl;

    v.clear();
    cout<<v.size()<<"  "<<v.capacity()<<endl; //0 12
    return 0;
}

  vector缺省使用alloc作为空间配置器,并据此另外定义了一个data_allocator,为的是更方便以元素大小为配置单位,data_allocator::deallocate(n)表示配置n个元素空间,vector提供许多constructors一个允许我们指定空间大小及初值。

/构造函数,允许指定vector大小n和初值value

vector(size_type n,const T& value){fill_initialize(n,value);}

//填充并初始化

void fill_initialize(size_type n,const T& value){ //用于vector初始赋值

start=allocate_and_fill(n,value);

finish=start+n;

end_of_storage=finish;

}

//配置空间,并填满内存

iterator allocate_and_fill(size_type n,const T& x){

iterator result=data_allocator::allocate(n)

/*全局函数,uninitialized_fill_n()有3个参数:

迭代器first指向欲初始化空间的地址的起始处

*初始化空间的大小n

*初始化的值x*/

uninitialized_fill_n(result,n,x);

return result;

}

  uninitialled_fill_n()会更根据第一参数类型来决定是使用算法fill_n()或反复调用construct();fill_n()多用于初始化多个相同数据,construct()多用于插入一个数据。

  当我们利用push_back()插入元素是,vector会先检查备用空间是否充足,如果充足,尾部插入数据,否则,先扩充空间,再插入数据,这里就要考虑到重新分配、移动数据、释放空间的问题。

void push_back(const T& x){//添加元素

if(finish !=end_of_storage)//是否超出最大可容纳空间{

/*全局函数,construct()接收一个指针p和一个初值value,该函数的用途就是将

初值value设定到指针锁指的空间上。

*/

construct(finish,x);

++finish;

}

else {

insert_aux(end(),x);  //vector的成员函数

}

}

insert_aux ()的具体实现如下:

template <class T,class Alloc>
void vector<T,Alloc>::insert_aux(iterator position, const T &x){
    if(finish!=end_of_storage){//还有备用空间
        //在备用空间起始处构造一个元素,并在vector最后一个元素值为其初值
        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;
        //以上配置原则:如果为0,则配置1,否则配置原来大小的2倍
        iterator new_start=data_allocator::allocate(len);//实际配置
        iterator new_finish=new_start;
        try{
            //将原vector元素拷贝过来
            new_finish=uninitialized_copy(start,position,new_start);
            //为新元素设置初值
            construct(new_finish,x);
            ++finish();
            //将原vector的备用空间中的内容也从新拷贝过来
            new_finish=uninitialized_copy(position,finish,new_finish);
        }
        catch(...){
            destory(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;
    }
}

可以看到,动态增加是直接开辟新的2倍的空间,进行数据的复制,而不是直接在原来vector后面开辟空间,因为无法保证其后面是否有足够空间,这也就说明指向原vector的所有迭代器就都失效了(查看示例)

p,li { white-space: pre-wrap }

原文地址:https://www.cnblogs.com/ybf-yyj/p/9615291.html

时间: 2024-07-29 22:46:12

vector源码2(参考STL源码--侯捷)的相关文章

vector源码1(参考STL源码--侯捷)

vector源码2(参考STL源码--侯捷) vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效 vector概述 Vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间纳入新元素,vector的使用效率,关键在于其对大小的控制以及重新配置时的元素迁移效率. Vector定义摘要 template <class T,class Alloc=alloc>//alloc是SGI STL的空间配置器 class vector { public: typedef T

list源码4(参考STL源码--侯捷):transfer、splice、merge、reverse、sort

list源码1(参考STL源码--侯捷):list节点.迭代器.数据结构 list源码2(参考STL源码--侯捷):constructor.push_back.insert list源码3(参考STL源码--侯捷):push_front.push_back.erase.pop_front.pop_back.clear.remove.unique list源码4(参考STL源码--侯捷):transfer.splice.merge.reverse.sort transfer list内部提供一个所

list源码1(参考STL源码--侯捷):list节点、迭代器、数据结构

list概述 list相对于vector复杂得多,list是一个双链表,对于插入元素.删除元素,list都相对比较简单 list节点 template <class T> struct __list_node{ typedef void* void_pointer; void_pointer prev; //类型为void*,其实也可以设置为__list_node<T>* void_pointer next; T data; }; list迭代器 list迭代器必须有能力指向lis

list源码2(参考STL源码--侯捷):constructor、push_back、insert

list的push_back.insert的使用如下: #include<bits/stdc++.h> using namespace std; int main() { int i; list<int> l; cout<<l.size()<<endl; //0 l.push_back(1); l.push_back(3); l.push_back(5); l.push_back(7); l.push_back(9); cout<<l.size(

vector源码(参考STL源码--侯捷)-----空间分配导致迭代器失效

vector源码1(参考STL源码--侯捷) vector源码2(参考STL源码--侯捷) #include<bits/stdc++.h> using namespace std; int main(){ vector<int> v(3,3); vector<int>::iterator it=v.begin(); cout<<v.size()<<" "<<v.capacity()<<endl;//3

C++ 《STL源码剖析》学习-vector

本文章是笔者学习<STL源码剖析>的学习笔记,记录的是笔者的个人理解,因为个人的水平有限,难免会有理解不当的地方,而且该书出版的时间比较久,难免会有些不一样.如有不当,欢迎指出. vector是c++中经常用到的数据结构,而且在面试时也会有提及,因此了解vector很重要. 一说到vector,我们就很容易想到另外一个与它十分相似的数据结构,关于它们之间显著的差别,我觉得是在于空间运用的灵活性上.数组是静态的,在声明的时候就要指明其具体的空间大小,而vector是动态的,随着元素的增加,它内部

STL源码--vector(一)

一.vector的特性 vector其中一个特点:内存空间只会增长,不会减小,援引C++ Primer:为了支持快速的随机访问,vector容器的元素以连续方式存放,每一个元素都紧挨着前一个元素存储.设想一下,当vector添加一个元素时,为了满足连续存放这个特性,都需要重新分配空间.拷贝元素.撤销旧空间,这样性能难以接受.因此STL实现者在对vector进行内存分配时,其实际分配的容量要比当前所需的空间多一些.就是说,vector容器预留了一些额外的存储区,用于存放新添加的元素,这样就不必为每

stl源码分析之vector

上篇简单介绍了gcc4.8提供的几种allocator的实现方法和作用,这是所有stl组件的基础,容器必须通过allocator申请分配内存和释放内存,至于底层是直接分配释放内存还是使用内存池等方法就不是组件需要考虑的事情.这篇文章开始分析gcc4.8 stl的容器源码实现.stl的容器分为序列式容器和关联式容器,前者包括vector,list,queue以及stack等常用数据结构,后者包含了map,set以及hash table等比较高级的结构,本文就从使用最广泛也是最基础的vector开始

STL源码剖析(4):容器(vector)

容器是很多人对STL的第一印象,vector,stack,queue,set,map等等都是容器. 这里先介绍 STL中的序列式容器. 所谓序列式容器,其中的元素可序(ordered),但未必有序(sorted).C++ 本身提供了一个序列式容器--数组(array),STL中还提供了向量(vector),链表(list),堆栈(stack),队列(queue),优先队列(priority queue)等,其中stack和queue只是将deque(双端队列)设限而得到的,技术上可以被归为一种配