深度剖析空间配置器(一)构造和析构函数
来源:互联网 发布:北京知果科技 编辑:程序博客网 时间:2024/06/05 17:39
STL空间配置器
STL为什么需要空间适配器
原因1、频繁的申请和释放空间,会降低运行的效率
频繁的申请和释放,就会频繁的调用malloc函数和free函数
调用函数会压栈出栈,这些都是有开销的
原因2、频繁的申请和释放空间,会造成内存碎片的问题
这就会造成,即使是有足够的内存(40字节),但是通过申请并不能得到一个连续24字节的内存
从而引入了空间配置器
空间适配器
概念
空间适配器是STL,标准模板库的六大组件之一,其余分别是:容器,迭代器,算法,配接器,仿函数
主要分三个文件实现,首先我们来介绍第一个文件stl_construct.h 这里定义了全局函数construct()和destroy(),负责对象的构造和析构。
先介绍构造函数 construct():
#include <new.h> //定位new 的头文件template <class _T1, class _T2>inline void _Construct(_T1* __p, const _T2& __value) { new ((void*) __p) _T1(__value); // placement new; 调用 _T1(__value)构造新对象到预分配的内存p上}template <class _T1>inline void _Construct(_T1* __p) { new ((void*) __p) _T1(); //无参函数构造新对象}
template <class _T1, class _T2>inline void construct(_T1* __p, const _T2& __value) { _Construct(__p, __value);}template <class _T1>inline void construct(_T1* __p) { _Construct(__p);}
placement new 允许你在一个已经分配好的内存中(栈或堆)构造一个新的对象,原型中 (void*) __p 实际上就是指向一个已经分配好的内存缓冲区的首地址。STL 借助C++中的 placement new 来提高效率,因为使用 new 操作符分配内存需要在堆中查找足够大的剩余空间,这个操作速度是很慢的,而且有可能出现无法分配内存的异常。借助 placement new 就可以解决这个问题,我们构造对象都是在一个预先准备好了的内存缓冲区中进行,不需要查找内存,内存分配的时间是常数,而且不会出现在程序运行中途出现内存不足的异常。
析构函数destroy():
//单个对象template <class _Tp>inline void _Destroy(_Tp* __pointer) { __pointer->~_Tp();}//迭代器版本template <class _ForwardIterator>void__destroy_aux(_ForwardIterator __first, _ForwardIterator __last, __false_type)//对象的析构函数不是无意义的,函数内部调用destroy() { for ( ; __first != __last; ++__first) destroy(&*__first); //调用destroy析构一个个对象}template <class _ForwardIterator> inline void __destroy_aux(_ForwardIterator, _ForwardIterator, __true_type) {} //对象的析构函数是无意义的,什么都不做。//这个函数判断对象的析构函数是否是 无意义的。template <class _ForwardIterator, class _Tp>inline void __destroy(_ForwardIterator __first, _ForwardIterator __last, _Tp*){ typedef typename __type_traits<_Tp>::has_trivial_destructor _Trivial_destructor; //判断析构函数是否 是无意义的。 __destroy_aux(__first, __last, _Trivial_destructor());}//这个函数判断迭代器所指向的对象的类型的指针。template <class _ForwardIterator>inline void _Destroy(_ForwardIterator __first, _ForwardIterator __last) { __destroy(__first, __last, __VALUE_TYPE(__first)); //__VALUE_TYPE(__first) 迭代器所指向对象的类型的指针。}//迭代器版本 destroy()_Destroy() -> __destroy() -> __destroy_aux() -> destroy() 指针版本 //所以实际上是逐个析构,只是中间为了效率,引入了判断元素的指针型别,来确定对象是否具备有用的析构函数
template <class _Tp>inline void destroy(_Tp* __pointer) { _Destroy(__pointer);}template <class _ForwardIterator>inline void destroy(_ForwardIterator __first, _ForwardIterator __last) { _Destroy(__first, __last);}
上面可以看出,对于迭代器版本的destroy() 实际最后都落脚到了单一对象指针版本的 destroy()。那么为什么要绕一大圈呢?因为这里接受的参数是两个迭代器,目的就是将这两个迭代器范围内的所有对象都析构掉,如果范围很大,并且对象的析构函数都是无关紧要的,那么一次次的调用这些个无用的析构函数,势必会对效率产生大的影响,所以先利用__VALUE_TYPE(__first) 获得迭代器所指对象的型别的指针,再利用 __type_traits<_Tp> 判断该型别的析构函数是否是无意义的,如果是则什么也不做,如果不是则循环逐个析构范围内的对象。
所以对象的构造实际是通过 placement new 完成的(缓存提前分配然后进行对象的分配);对象的析构则通过调用外在对象的析构函数(~_Tp())。注意的是,这里的析构只是析构对象,分配好的缓存并没有释放,所以可以反复利用缓存并给它分配对象。不打算再次使用这个缓存时,你可以delete 将其释放掉。
另外STL(v3.3)版本destroy() 还针对迭代器类型为定义了特化版本
inline void _Destroy(char*, char*) {}inline void _Destroy(int*, int*) {}inline void _Destroy(long*, long*) {}inline void _Destroy(float*, float*) {}inline void _Destroy(double*, double*) {}inline void _Destroy(wchar_t*, wchar_t*) {}
0 0
- 深度剖析空间配置器(一)构造和析构函数
- 《STL源码剖析》-空间配置器(一)构造和析构
- 【深度探索STL】空间配置器(一) 构造和析构
- 深度剖析空间配置器(三)内存处理函数
- STL源码剖析 [特殊的空间配置器](对象构造和析构stl_construct.h)
- 深度剖析空间配置器(二)一二级配置器
- 深度剖析C++全局构造函数和析构函数的调用机制
- 【STL】空间配置器剖析(一)
- 《STL剖析》——空间配置器(一)
- stl源码剖析(一)空间配置器
- 构造函数和析构函数 <一>
- STL源码:空间配置器(二)构造和析构construct()/destroy()
- C++ 拷贝构造函数深度剖析
- C++ (构造函数、析构函数、动态申请空间)
- 深度剖析C++析构函数
- 【STL】空间配置器剖析(二)
- 【STL】空间配置器剖析(完结)
- STL 源码剖析读书笔记一:空间配置器
- Android Studio更新gradle版本
- 二叉树的所有路径
- MVC3使用MvcPager实现简单分页功能
- 构建微服务 spring boot
- SpringMVC,Spring MVC是一个基于Model2的MVC框架
- 深度剖析空间配置器(一)构造和析构函数
- Linux进程间通信——使用共享内存
- chromium源码如何启用USE_ASH预定义宏
- 搭建一个支持HTTPS的私有DOCKER Registry
- JavaScript设置按下enter键后执行函数
- 微服务实战:深入微服务架构的进程间通信
- STL 基础学习总结
- Maven Intellij 配置下载镜像
- shell-四(数组和函数)