一.六大组件
- 容器(Containers):各种数据结构,如Vector,List,Deque,Set,Map,用来存放数据
- 算法(Algorithms):各种常用算法如Sort,Search,Copy,Erase,从实现的角度来看,STL算法是一种Function Templates
- 迭代器(Iterators):扮演容器与算法之间的胶合剂,是所谓的“泛型指针”,共有五种类型,以及其它衍生变化,从实现的角度来看,迭代器是一种将:Operators*,Operator->,Operator++,Operator–等相关操作予以重载的Class Template。
- 仿函数(Functors): 行为类似函数,可作为算法的某种策略,常配合具体算法使用。
- 配接器(适配器):一种用来修饰容器或仿函数或迭代器接口的东西,例如queue和stack。
- 分配器(Allocators):负责空间配置与管理,从实现的角度来看,配置器是一个实现了动态空间配置、空间管理、空间释放的Class Template。
二.六大组件的关系
容器使用分配器分配数据存储的空间,算法通过迭代器存取容器内的内容,仿函数可以协助算法实现不同的策略,配接器可以修饰或者套接仿函数
三.allocator
- 实现一个简单的配置器(不符合规范),常常只需要定义一个allocator的模板类,然后在底层调用operator new()函数与operator delete()函数即可。
- new和delete关键字的操作过程
new 先调用底层的operator new()分配空间,然后调用construct全局函数构造对象;delete 先调用destroy函数析构对象,然后调用 operator delete()释放空间。
注意:destroy一般来说有两个版本,当析构一个对象时直接调用对象析构函数即可,若是多个对象则先判断析构函数是否是一些无关痛痒的析构函数(即所谓的trivial destructor
),若是的话就弃之不顾了,否则就需要一个个的调用对应的析构函数了,这大大提高了效率。 - 空间配置器的分级
当用户申请的空间大于等于128个字节时,使用一级空间配置器。当用户申请的空间小于128个字节时,使用二级空间配置器。一级空间配置器的底层实现malloc,二级空间配置器的底层实现是内存池,其通过维护16个自由链表,负责16种小型区块的次配置。
- 《STL源码剖析》中关于set_new_handler的理解
- 二级配置器自由列表原理
本来也没什么好说的,只是它这个自由链表的使用方式实在是骚,使用union结构,有效减少了内存消耗,既可以被我们使用obj.free_list_link作为一个指向块大小皆为8的倍数的链表,同时该变量又是真正的物理内存该链表的起始位置,是真的妙啊。。。
至于16个自由链表管理的块大小如下表所示:
free_list | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
size(bytes) | 8 | 16 | 24 | 32 | 40 | 48 | 56 | 64 | 72 | 80 | 88 | 96 | 104 | 112 | 120 | 128 |
- 简单说一说分配流程
- 对于allocate当大于128bytes时在一级配置器里面分配内存,具体就是调用malloc,而当小于时就在这个free_list中查找合适的块,如果没有的话就向上取整到8的倍数的边界,返回合适的区块即可。最后调用refill为free_list重新填充区块。
- 对于deallocate就比较简单了,大于128bytes使用一级配置器,底层调用free释放。对于小于128bytes的则直接找到对应的free_list接在后面即可。
四.内存池的设计实现
其实本质就是在自由链表的下层又搞了一层缓存,当自由链表没有了对应的块,甚至更大的块也找不到的时候就去内存池里面找,若是内存池里面也找不到对应的块,那么就调用malloc看还有没有物理内存可以使用,有的话就申请过来些,放置一半在内存池,另外的交给自由链表,自由链表交一个给客户,并将剩余的东西自己维护起来。最后若是物理内存里面也找不到物理块了就抛出bad_alloc异常。
五.内存基本处理工具
其实就是需要我们掌握以下5个对于学习STL源码异常重要的全局函数:
- construct()
- destory()
- uninitialized_copy() 对应STL高层函数copy()
- uninitialized_fill() 对应STL高层函数fill()
- uninitialized_fill_n() 对应STL高层函数fill_n()
按照STL书所讲的东西来看,基本后三个函数与他们对应封装的高级算法表现出一样的作用,所以此处,直接学习三个STL的高级算法吧,以下代码皆来自cplusplus.com网站:
1.copy()函数
template<class InputIterator, class OutputIterator>
OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result)
{
while (first!=last) {
*result = *first;
++result; ++first;
}
return result;
}
具体示例:
// copy algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::copy
#include <vector> // std::vector
int main () {
int myints[]={10,20,30,40,50,60,70};
std::vector<int> myvector (7);
std::copy ( myints, myints+7, myvector.begin() );
std::cout << "myvector contains:";
for (std::vector<int>::iterator it = myvector.begin(); it!=myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0; //输出:myvector contains: 10 20 30 40 50 60 70
}
2.fill()函数
template <class ForwardIterator, class T>
void fill (ForwardIterator first, ForwardIterator last, const T& val)
{
while (first != last) {
*first = val;
++first;
}
}
示例如下:
// fill algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::fill
#include <vector> // std::vector
int main () {
std::vector<int> myvector (8); // myvector: 0 0 0 0 0 0 0 0
std::fill (myvector.begin(),myvector.begin()+4,5); // myvector: 5 5 5 5 0 0 0 0
std::fill (myvector.begin()+3,myvector.end()-2,8); // myvector: 5 5 5 8 8 8 0 0
std::cout << "myvector contains:";
for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
//输出:myvector contains: 5 5 5 8 8 8 0 0
return 0;
}
3.fill_n()函数
template <class OutputIterator, class Size, class T>
OutputIterator fill_n (OutputIterator first, Size n, const T& val)
{
while (n>0) {
*first = val;
++first; --n;
}
return first; // since C++11
}
示例如下:
// fill_n example
#include <iostream> // std::cout
#include <algorithm> // std::fill_n
#include <vector> // std::vector
int main () {
std::vector<int> myvector (8,10); // myvector: 10 10 10 10 10 10 10 10
std::fill_n (myvector.begin(),4,20); // myvector: 20 20 20 20 10 10 10 10
std::fill_n (myvector.begin()+3,3,33); // myvector: 20 20 20 33 33 33 10 10
std::cout << "myvector contains:";
for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
//输出:myvector contains: 20 20 20 33 33 33 10 10
return 0;
}