目录
- 一、介绍
- 二、C++内存操作
- 三、对象构造和析构
-
-
- 3.1、对象构造
- 3.2、对象析构
-
- 3.2.1 析构单个对象
- 3.2.2 析构指定范围内的对象
-
- 3.2.2.1、_Destroy(_ForwardIterator __first, _ForwardIterator __last)
- 3.2.2.2、_Destroy_n(_ForwardIterator __first, _Size __count)
- 3.2.2.3、_Destroy(_ForwardIterator __first, _ForwardIterator __last, _Allocator &__alloc)
- 3.2.2.4、_Destroy(_ForwardIterator __first, _ForwardIterator __last, allocator<_Tp> &)
- 3.3 _Construct和_Destroy示意
-
- 四、具体配置器示例
本文以gcc_9.3.0为例
一、介绍
配置器的基本目的是为给定类型提供内存资源以及提供在内存不再需要时将其归还的地方。标准库的内存管理被封装在一个类模板中——allocator。
1.1、设计
C++标准对于该领域只是给出了一些指示,实现这些要求的最简单的方式是调用operator new
和operator delete
运算符。相比于有缓存的分配和可以重复利用之前分配的内存,这种方法可能会比较慢。但是这种方法可以在多种硬件和操作系统中正确地工作。__gnu_cxx::new_allocator
实现了简单的operator new
和operator delete
语义,而__gun_cxx::malloc_allocator
使用C语言的函数std::malloc
和std::free
实现了相同的功能。
另一种方法是在配置器类中使用智能来缓存分配。 这种额外的机制可以采用多种形式:位图索引,以2的指数级增长的存储桶的索引,或者更简单的固定大小缓存池。缓存可以在程序中的容器间共享。而且,也不总是调用operator new和operator delete来传递内存,这可以带来速度上的优势。使用这些技术的配置器是__gnu_cxx::bitmap_allocator
,__gun_cxx::mt_alloc
,和__gun_cxx::pool_allocator
。1
1.2、使用
STL默认使用std::allocator
配置器,而该配置器继承自__gnu_cxx::new_allocator
。
vector< int > int_vec;
我们可以指定配置器:
vector<int,__gnu_cxx::malloc_allocator<int>> malloc_intvec;
1.3、扩展的配置器
- 1、new_allocator:简单地包装::operator new 和::operator delete
- 2、malloc_allocator:简单地包装malloc和free
- 3、debug_allocator:在一个任意的配置器A外增加了一层包装。它将大小略有增加的请求传递给A,并使用额外的内存来存储尺寸大小信息。当将指针传递给deallocate()时,将检查存储的大小,并使用assert()确保它们匹配。
- 4、throw_allocator:包含内存跟踪和标记功能
- 5、__pool_alloc:高性能的单池配置器。可重用内存在类型相同实例之间共享。当列表(free list)被用完时,它将通过::operator new 获取新内存。如果客户容器请求的块超过某个阈值,则将绕过缓冲池,将分配请求直接传递给::operator new。
- 6、__mt__alloc:高性能的固定尺寸的配置器,分配呈指数级增长。
- 7、bitmap_allocator:一种高性能的配置器,使用位图来跟踪已经使用和未使用的内存位置。
二、C++内存操作
2.1、C++ 中的 new 和 delete
当我们使用一条new表达式时:
string *sp = new string("a value");
string *arr = new string[10];
实际执行了三步操作:
- 第一步,new表达式调用
operator new
(或者operator new[ ]
)标准库函数。该函数分配一块足够大的、原始的、未命名的内存空间以便存储特定类型的对象(或者对象的数组)。 - 第二步,编译器运行相应的构造函数以构造这些对象,并为其传入初始值。
- 第三步,对象被分配了空间并构造完成,返回一个指向该对象的指针。
当我们使用一条delete表达式删除一个动态分配的对象时:
delete sp;
delete [] arr;
实际执行了两步操作:
- 第一步,对sp所指向的对象或者arr所指的数组中的元素执行对应的析构函数。
- 第二步,编译器调用名为
operator delete
(或者operator delete[ ]
)的标准库函数释放内存空间。2
2.2、STL内存配置
STL将new和delete的实现均分为两个阶段:
- 内存配置由
allocator::allocate()
负责,内存释放由allocator::deallocate()
负责 - 对象构造由
_Construct()
负责,对象析构由_Destroy()
负责
allocate和deallocate在不同的配置器中有不同的实现,_Construct和_Destroy则在stl_construct.h中实现。
三、对象构造和析构
3.1、对象构造
调用placement new
/**
* Constructs an object in existing memory by invoking an allocated
* object's constructor with an initializer.
*/
#if __cplusplus >= 201103L
template <typename _T1, typename... _Args>
inline void
_Construct(_T1 *__p, _Args &&... __args)
{
::new (static_cast<void *>(__p)) _T1(std::forward<_Args>(__args)...);
}
#else
template <typename _T1, typename _T2>
inline void
_Construct(_T1 *__p, const _T2 &__value)
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 402. wrong new expression in [some_]allocator::construct
::new (static_cast<void *>(__p)) _T1(__value);
}
#endif
3.2、对象析构
3.2.1 析构单个对象
/**
* Destroy the object pointed to by a pointer type.
*/
template <typename _Tp>
inline void
_Destroy(_Tp *__pointer)
{
__pointer->~_Tp();
}
3.2.2 析构指定范围内的对象
3.2.2.1、_Destroy(_ForwardIterator __first, _ForwardIterator __last)
template <bool>
struct _Destroy_aux
{
template <typename _ForwardIterator>
static void
__destroy(_ForwardIterator __first, _ForwardIterator __last)
{
for (; __first != __last; ++__first)
std::_Destroy(std::__addressof(*__first));
}
};
template <>
struct _Destroy_aux<true>
{
template <typename _ForwardIterator>
static void
__destroy(_ForwardIterator, _ForwardIterator) {
}
};
/**
* Destroy a range of objects. If the value_type of the object has
* a trivial destructor, the compiler should optimize all of this
* away, otherwise the objects' destructors must be invoked.
*/
template <typename _ForwardIterator>
inline void
_Destroy(_ForwardIterator __first, _ForwardIterator __last)
{
typedef typename iterator_traits<_ForwardIterator>::value_type
_Value_type;
#if __cplusplus >= 201103L
// A deleted destructor is trivial, this ensures we reject such types:
static_assert(is_destructible<_Value_type>::value,
"value type is destructible");
#endif
std::_Destroy_aux<__has_trivial_destructor(_Value_type)>::
__destroy(__first, __last);
}
3.2.2.2、_Destroy_n(_ForwardIterator __first, _Size __count)
template <bool>
struct _Destroy_n_aux
{
template <typename _ForwardIterator, typename _Size>
static _ForwardIterator
__destroy_n(_ForwardIterator __first, _Size __count)
{
for (; __count > 0; (void)++__first, --__count)
std::_Destroy(std::__addressof(*__first));
return __first;
}
};
template <>
struct _Destroy_n_aux<true>
{
template <typename _ForwardIterator, typename _Size>
static _ForwardIterator
__destroy_n(_ForwardIterator __first, _Size __count)
{
std::advance(__first, __count);
return __first;
}
};
/**
* Destroy a range of objects. If the value_type of the object has
* a trivial destructor, the compiler should optimize all of this
* away, otherwise the objects' destructors must be invoked.
*/