设为首页 加入收藏

TOP

STL源码分析之空间配置器(一)
2018-12-04 22:08:59 】 浏览:16
Tags:STL 源码 分析 空间 配置

#### 前言

SGI STL将new的申请空间和调用构造函数的两个功能分开实现, 如果对new不太清楚的, 可以先去看看这一篇[new实现](https://blog.csdn.net/Function_Dou/article/details/84526761)再来看配置器也不迟. 本节是STL分析的第一篇, 主要分析STL各个部分都会出现的`alloc`实现, 虽然每个部分都只会默认调用它, 不了解它也可以看懂分析, 但是他又是不可缺少的, 我们就以它做为开篇进行分析.

 

#### "new"的实现

这里直接我们直接来看STL的`construct`实现吧

```c++
// 这里的construct调用的是placement new, 在一个已经获得的内存里建立一个对象
template <class T1, class T2>
inline void construct(T1* p, const T2& value)
{
new (p) T1(value);
}
```

可以明白这里就只是一个`placement new`的调用, 只是用了泛型来实现一个对象分配的模板, 并实现初始化.

既然已经看到了对象的分配, 那再接再厉看看空间的分配, 充分了解STL是怎么将new分开执行的. allocate函数实现空间的申请, 但是这里有一点看不出来, 申请内存是有分为一级配置器和二级配置器, 分配的空间小于128字节的就调用二级配置器, 大于就直接使用一级配置器, 一级配置器直接调用`malloc`申请, 二级使用内存池.

```c++
template<class T>
inline T* allocate(ptrdiff_t size, T*)
{
set_new_handler(0);
T* tmp = (T*)(::operator new(size)(size * sizeof(T)));
if(!tmp)
{
cerr << "out of memort" << endl;
exit(1);
}
return tmp;
}
```

内存分配果然是调用`operator new`来执行空间分配, 这里allocate和construct都只是简单的对`operator new`, `placement new`进行封装.

```c++
const int N = 4;
int main()
{
allocator<string> alloc;
auto str_ve = alloc.allocate(N);
auto p = str_ve; // vector<string> *p = str_ve;
alloc.construct(p++);
alloc.construct(p++, 10, 'a');
alloc.construct(p++, "construct");
cout << str_ve[0] << endl;
cout << str_ve[1] << endl;
cout << str_ve[2] << endl;

while(p != str_ve)
{
alloc.destroy(--p);
}
alloc.deallocate(str_ve, N);

exit(0);
}
```

输出结果为

```shell
rpz@0505:stl3.0$ ./a.out

aaaaaaaaaa
construct
```

这个程序首先调用`allocate`申请N个大小的空间, 在依次`construct`调用构造函数, 这里就先初始化3个结构, 紧接着通过`destory`调用析构函数, 最后`deallocate`释放申请的空间. 整个过程很容易理解, 但是这里还要深入是dealllocate和destroy两个函数.

 

#### "delete"实现

先是看`destroy`调用析构函数. 而destroy有两个版本.

**版本一:**

需要传入的参数 : **一个指针**

```c++
// 第一版本, 接收指针
template <class T> inline void destroy(T* pointer)
{
pointer->~T();
}
```

版本一直接就调用了析构函数, 不用过多的分析.

 

**版本二:**

需要传入的参数 : **两个迭代器**

```c++
// 第二个版本的, 接受两个迭代器, 并设法找出元素的类型. 通过__type_trais<> 找出最佳措施
template <class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last)
{
__destroy(first, last, value_type(first));
}

// 接受两个迭代器, 以__type_trais<> 判断是否有traival destructor
template <class ForwardIterator, class T>
inline void __destroy(ForwardIterator first, ForwardIterator last, T*)
{
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
__destroy_aux(first, last, trivial_destructor());
}
```

destroy直接调用`__destroy`, 前者只是一个接口, 所以重点是在后者.

分析`__type_traits<>` : 它是用于获取迭代器所指对象的类型,运用traits技法实现的.只要记住我们用来获取对对象类型就可以了. 然后通过类型的不一样选择执行不同的析构调用.

 

当`__type_traits `为`__false_type`时, 调用的是下面这个函数, 通过迭代所有的对象并调用版本一的函数执行析构函数进行析构. 而这个是被称为`non-travial destructor `

```c++
// 没有non-travial destructor
template <class ForwardIterator>
inline void __destroy_aux(ForwardIterator first, ForwardIterator last, __false_type)
{
for ( ; first < last; ++first)
destroy(&*first);
}
```

 

当`__type_traits `为`__true_type`时, 什么也不做, 因为这样效率很高效, 并不需要执行析构函数. 而这个是被称为`travial destructor `.

```c++
// 有travial destructor
template <class ForwardIterator>
inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}
```

 

最后是版本二的特化
编程开发网

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇new实现 下一篇开源C++版本CGI库CGICC入门

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

array(4) { ["type"]=> int(8) ["message"]=> string(24) "Undefined variable: jobs" ["file"]=> string(32) "/mnt/wp/cppentry/do/bencandy.php" ["line"]=> int(214) }