空间配置器(allocator)(重点)
背景需求:在底层默默的实现空间的分配
问题:空间的申请与对象的创建两者分开,因为不断创建的时候可能会频繁的申请空间扩容。
类似操作:reserve函数,new表达式,都是先申请空间然后再创建对象。
空间配置器的操作:
//空间的申请,申请的是原始的,未初始化的空间
pointer allocate( size_type n, const void * hint = 0 );
T* allocate( std::size_t n, const void * hint);
T* allocate( std::size_t n );
//空间的释放
void deallocate( T* p, std::size_t n );
//对象的构建,在指定的未初始化的空间上构建对象,使用的是定位new表达式
void construct( pointer p, const_reference val );
//对象的销毁
void destroy( pointer p );
【注意】为什么还需要将对象进行单独的销毁,直接随着空间的销毁而销毁就好了。因为有可能有堆上的空间,需要通过销毁对象可以删除堆上的空间。
当空间小于128的时候,就认为是小的空间,可以一次性将空间申请下来然后再划分为小的片段。1.也是避免出现碎片化的问题。可能出现空间太过于碎片化,不能申请下来。
2.申请空间需要进入内核态,避免在内核态和用户态之间频繁切换。
主要的函数是在于s_refill 和s_chunk函数
其实申请了1280空间,其中的640字节开头会交给_free_start指针表示结尾由end指示,会返回回来640个字节,并且将每个分为32字节。其中的一个32字节会被返回,剩下的19个32字节会通过一个链表链接起来。挂接在下标为3的自由链表下面。
chunk其中的640字节会交给refill函数处理,剩下的640交给自由链表
下次再申请的时候64字节块再申请的640字节直接交给refill,而不是再申请空间,因为原本的s_start_free 和 s_end_free在32字节块划分完成以后差值是640,在64划分完以后,两者又相等了。
这个时候自由链表中下标为3的下面就是19个32字节的字节快,64下面是链接9个字节快。
当没有足够的空间的时候,会向上申请,有可能有80,88,96等有这样的数据块。有这样的块的话就拆下来一个借给其他节点。将96拆成72和24,用内存池的两个指针进行指向继续24的开头和结尾。并没有从找两块64拼接起来,因为可能这两块并不是连续的,因为可能会回收,回收的时候顺序就不一定了。
改变了nobjs的数值为1直接就返回nobjs
构建对象使用到定位new表达式:
new(__p) _Tp(_val);
销毁对象: __p->~Tp();
对比理解就是vector<int> vec(3) = {1,2,3};
int arr[3] = {1,2,3}; vec取地址不能取到第一个元素的地址,但是arr取地址可以得到第一个元素的地址,因为vec的元素是在堆上的,arr的元素是在栈上的