容器本质上的作用就是对数据的管理,整体来说就是对数据的增、删、修改;但是结合了计算机的空间属性,就又需要进行一次扩展,也就是对于计算机空间的管理:申请与释放;
关于new操作原理:
对于一个元素的话,整个过程可以参考new的过程,且STL默认的空间分配器就是以new作为基础进行实现,new的话,直接使用它会申请需要的空间并调用对象的构造函数并返回构造好对象的空间地址;他是分为两个过程的,一个过程是对空间的申请,另外一个过程是对申请到的空间的对象构造;在C++中分别是operator new与placement new;
容器基于这样的空间分配方式以及对象构造方式进行对数据的存储以及管理;
STL空间分配器:
stl默认的空间分配器为std::allocator;该类为一个模板类,对应的源码位置为libstdc++-v3/include/bits/allocator.h;该模板类继承另外一个模板类,__allocator_base,该基类为一个宏,在libstdc++-v3/include/bits/c++allocator.h中定义,最终该基类指向/libstdc++-v3/include/ext/new_allocator.h;里面有几个接口,这些接口基本原理就是operator new和placement new的具体使用;而rebind则是该空间分配器绑定另外一个类型进行返回。该空间分配器allocator接口的作用就是基于_Tp作为基本单元,而后申请需要单位数的空间;该类源码如下:
template<typename _Tp>
class new_allocator
{
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef const _Tp* const_pointer;
typedef _Tp& reference;
typedef const _Tp& const_reference;
typedef _Tp value_type;
template<typename _Tp1>
struct rebind
{
/* 重新绑定空间分配器到其他类型上 */
typedef new_allocator<_Tp1> other;
};
#if __cplusplus >= 201103L
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2103. propagate_on_container_move_assignment
typedef std::true_type propagate_on_container_move_assignment;
#endif
new_allocator() _GLIBCXX_USE_NOEXCEPT { }
new_allocator(const new_allocator&) _GLIBCXX_USE_NOEXCEPT { }
template<typename _Tp1>
new_allocator(const new_allocator<_Tp1>&) _GLIBCXX_USE_NOEXCEPT { }
~new_allocator() _GLIBCXX_USE_NOEXCEPT { }
pointer
address(reference __x) const _GLIBCXX_NOEXCEPT
{ return std::__addressof(__x); }
const_pointer
address(const_reference __x) const _GLIBCXX_NOEXCEPT
{ return std::__addressof(__x); }
// NB: __n is permitted to be 0. The C++ standard says nothing
// about what the return value is when __n == 0.
/** 申请到堆空间返回申请到空间的指针 */
pointer allocate(size_type __n, const void* = 0)
{
if (__n > this->max_size())
std::__throw_bad_alloc();
return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
}
// __p is not permitted to be a null pointer.
void
deallocate(pointer __p, size_type)
{
::operator delete(__p);
}
size_type
max_size() const _GLIBCXX_USE_NOEXCEPT
{ return size_t(-1) / sizeof(_Tp); }
#if __cplusplus >= 201103L
template<typename _Up, typename... _Args>
void
construct(_Up* __p, _Args&&... __args)
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
template<typename _Up>
void
destroy(_Up* __p) { __p->~_Up(); }
#else
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 402. wrong new expression in [some_] allocator::construct
/* placement new函数,也就是调用构造函数去初始化申请到的空间 */
void
construct(pointer __p, const _Tp& __val)
{ ::new((void *)__p) _Tp(__val); }
/* 调用析构函数 */
void
destroy(pointer __p) { __p->~_Tp(); }
#endif
};
/* 利用template模板去判断两个分配器是否是相同的分配器 */
template<typename _Tp>
inline bool
operator==(const new_allocator<_Tp>&, const new_allocator<_Tp>&)
{
return true;
}
template<typename _Tp>
inline bool
operator!=(const new_allocator<_Tp>&, const new_allocator<_Tp>&)
{
return false;
}
这样,通过对_Tp的类型绑定,在申请内存的时候直接申请需要的单位即可;