📄 stl_deque.h
字号:
//standard container and at the same time makes use of the EBO
//for empty allocators.
struct _Deque_impl
: public _Alloc {
_Tp** _M_map;
size_t _M_map_size;
iterator _M_start;
iterator _M_finish;
_Deque_impl(const _Alloc& __a)
: _Alloc(__a), _M_map(0), _M_map_size(0), _M_start(), _M_finish()
{ }
};
typedef typename _Alloc::template rebind<_Tp*>::other _Map_alloc_type;
_Map_alloc_type _M_get_map_allocator() const
{ return _Map_alloc_type(this->get_allocator()); }
_Tp*
_M_allocate_node()
{ return _M_impl._Alloc::allocate(__deque_buf_size(sizeof(_Tp))); }
void
_M_deallocate_node(_Tp* __p)
{ _M_impl._Alloc::deallocate(__p, __deque_buf_size(sizeof(_Tp))); }
_Tp**
_M_allocate_map(size_t __n)
{ return _M_get_map_allocator().allocate(__n); }
void
_M_deallocate_map(_Tp** __p, size_t __n)
{ _M_get_map_allocator().deallocate(__p, __n); }
protected:
void _M_initialize_map(size_t);
void _M_create_nodes(_Tp** __nstart, _Tp** __nfinish);
void _M_destroy_nodes(_Tp** __nstart, _Tp** __nfinish);
enum { _S_initial_map_size = 8 };
_Deque_impl _M_impl;
};
template<typename _Tp, typename _Alloc>
_Deque_base<_Tp,_Alloc>::~_Deque_base()
{
if (this->_M_impl._M_map)
{
_M_destroy_nodes(this->_M_impl._M_start._M_node, this->_M_impl._M_finish._M_node + 1);
_M_deallocate_map(this->_M_impl._M_map, this->_M_impl._M_map_size);
}
}
/**
* @if maint
* @brief Layout storage.
* @param num_elements The count of T's for which to allocate space
* at first.
* @return Nothing.
*
* The initial underlying memory layout is a bit complicated...
* @endif
*/
template<typename _Tp, typename _Alloc>
void
_Deque_base<_Tp,_Alloc>::_M_initialize_map(size_t __num_elements)
{
size_t __num_nodes = __num_elements / __deque_buf_size(sizeof(_Tp)) + 1;
this->_M_impl._M_map_size = std::max((size_t) _S_initial_map_size,
__num_nodes + 2);
this->_M_impl._M_map = _M_allocate_map(this->_M_impl._M_map_size);
// For "small" maps (needing less than _M_map_size nodes), allocation
// starts in the middle elements and grows outwards. So nstart may be
// the beginning of _M_map, but for small maps it may be as far in as
// _M_map+3.
_Tp** __nstart = this->_M_impl._M_map + (this->_M_impl._M_map_size - __num_nodes) / 2;
_Tp** __nfinish = __nstart + __num_nodes;
try
{ _M_create_nodes(__nstart, __nfinish); }
catch(...)
{
_M_deallocate_map(this->_M_impl._M_map, this->_M_impl._M_map_size);
this->_M_impl._M_map = 0;
this->_M_impl._M_map_size = 0;
__throw_exception_again;
}
this->_M_impl._M_start._M_set_node(__nstart);
this->_M_impl._M_finish._M_set_node(__nfinish - 1);
this->_M_impl._M_start._M_cur = _M_impl._M_start._M_first;
this->_M_impl._M_finish._M_cur = this->_M_impl._M_finish._M_first + __num_elements
% __deque_buf_size(sizeof(_Tp));
}
template<typename _Tp, typename _Alloc>
void
_Deque_base<_Tp,_Alloc>::_M_create_nodes(_Tp** __nstart, _Tp** __nfinish)
{
_Tp** __cur;
try
{
for (__cur = __nstart; __cur < __nfinish; ++__cur)
*__cur = this->_M_allocate_node();
}
catch(...)
{
_M_destroy_nodes(__nstart, __cur);
__throw_exception_again;
}
}
template<typename _Tp, typename _Alloc>
void
_Deque_base<_Tp,_Alloc>::_M_destroy_nodes(_Tp** __nstart, _Tp** __nfinish)
{
for (_Tp** __n = __nstart; __n < __nfinish; ++__n)
_M_deallocate_node(*__n);
}
/**
* @brief A standard container using fixed-size memory allocation and
* constant-time manipulation of elements at either end.
*
* @ingroup Containers
* @ingroup Sequences
*
* Meets the requirements of a <a href="tables.html#65">container</a>, a
* <a href="tables.html#66">reversible container</a>, and a
* <a href="tables.html#67">sequence</a>, including the
* <a href="tables.html#68">optional sequence requirements</a>.
*
* In previous HP/SGI versions of deque, there was an extra template
* parameter so users could control the node size. This extension turned
* out to violate the C++ standard (it can be detected using template
* template parameters), and it was removed.
*
* @if maint
* Here's how a deque<Tp> manages memory. Each deque has 4 members:
*
* - Tp** _M_map
* - size_t _M_map_size
* - iterator _M_start, _M_finish
*
* map_size is at least 8. %map is an array of map_size pointers-to-"nodes".
* (The name %map has nothing to do with the std::map class, and "nodes"
* should not be confused with std::list's usage of "node".)
*
* A "node" has no specific type name as such, but it is referred to as
* "node" in this file. It is a simple array-of-Tp. If Tp is very large,
* there will be one Tp element per node (i.e., an "array" of one).
* For non-huge Tp's, node size is inversely related to Tp size: the
* larger the Tp, the fewer Tp's will fit in a node. The goal here is to
* keep the total size of a node relatively small and constant over different
* Tp's, to improve allocator efficiency.
*
* **** As I write this, the nodes are /not/ allocated using the high-speed
* memory pool. There are 20 hours left in the year; perhaps I can fix
* this before 2002.
*
* Not every pointer in the %map array will point to a node. If the initial
* number of elements in the deque is small, the /middle/ %map pointers will
* be valid, and the ones at the edges will be unused. This same situation
* will arise as the %map grows: available %map pointers, if any, will be on
* the ends. As new nodes are created, only a subset of the %map's pointers
* need to be copied "outward".
*
* Class invariants:
* - For any nonsingular iterator i:
* - i.node points to a member of the %map array. (Yes, you read that
* correctly: i.node does not actually point to a node.) The member of
* the %map array is what actually points to the node.
* - i.first == *(i.node) (This points to the node (first Tp element).)
* - i.last == i.first + node_size
* - i.cur is a pointer in the range [i.first, i.last). NOTE:
* the implication of this is that i.cur is always a dereferenceable
* pointer, even if i is a past-the-end iterator.
* - Start and Finish are always nonsingular iterators. NOTE: this means that
* an empty deque must have one node, a deque with <N elements (where N is
* the node buffer size) must have one node, a deque with N through (2N-1)
* elements must have two nodes, etc.
* - For every node other than start.node and finish.node, every element in
* the node is an initialized object. If start.node == finish.node, then
* [start.cur, finish.cur) are initialized objects, and the elements outside
* that range are uninitialized storage. Otherwise, [start.cur, start.last)
* and [finish.first, finish.cur) are initialized objects, and [start.first,
* start.cur) and [finish.cur, finish.last) are uninitialized storage.
* - [%map, %map + map_size) is a valid, non-empty range.
* - [start.node, finish.node] is a valid range contained within
* [%map, %map + map_size).
* - A pointer in the range [%map, %map + map_size) points to an allocated
* node if and only if the pointer is in the range
* [start.node, finish.node].
*
* Here's the magic: nothing in deque is "aware" of the discontiguous
* storage!
*
* The memory setup and layout occurs in the parent, _Base, and the iterator
* class is entirely responsible for "leaping" from one node to the next.
* All the implementation routines for deque itself work only through the
* start and finish iterators. This keeps the routines simple and sane,
* and we can use other standard algorithms as well.
* @endif
*/
template<typename _Tp, typename _Alloc = allocator<_Tp> >
class deque : protected _Deque_base<_Tp, _Alloc>
{
// concept requirements
__glibcxx_class_requires(_Tp, _SGIAssignableConcept)
typedef _Deque_base<_Tp, _Alloc> _Base;
public:
typedef _Tp value_type;
typedef typename _Alloc::pointer pointer;
typedef typename _Alloc::const_pointer const_pointer;
typedef typename _Alloc::reference reference;
typedef typename _Alloc::const_reference const_reference;
typedef typename _Base::iterator iterator;
typedef typename _Base::const_iterator const_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef typename _Base::allocator_type allocator_type;
protected:
typedef pointer* _Map_pointer;
static size_t _S_buffer_size()
{ return __deque_buf_size(sizeof(_Tp)); }
// Functions controlling memory layout, and nothing else.
using _Base::_M_initialize_map;
using _Base::_M_create_nodes;
using _Base::_M_destroy_nodes;
using _Base::_M_allocate_node;
using _Base::_M_deallocate_node;
using _Base::_M_allocate_map;
using _Base::_M_deallocate_map;
/** @if maint
* A total of four data members accumulated down the heirarchy.
* May be accessed via _M_impl.*
* @endif
*/
using _Base::_M_impl;
public:
// [23.2.1.1] construct/copy/destroy
// (assign() and get_allocator() are also listed in this section)
/**
* @brief Default constructor creates no elements.
*/
explicit
deque(const allocator_type& __a = allocator_type())
: _Base(__a, 0) {}
/**
* @brief Create a %deque with copies of an exemplar element.
* @param n The number of elements to initially create.
* @param value An element to copy.
*
* This constructor fills the %deque with @a n copies of @a value.
*/
deque(size_type __n, const value_type& __value,
const allocator_type& __a = allocator_type())
: _Base(__a, __n)
{ _M_fill_initialize(__value); }
/**
* @brief Create a %deque with default elements.
* @param n The number of elements to initially create.
*
* This constructor fills the %deque with @a n copies of a
* default-constructed element.
*/
explicit
deque(size_type __n)
: _Base(allocator_type(), __n)
{ _M_fill_initialize(value_type()); }
/**
* @brief %Deque copy constructor.
* @param x A %deque of identical element and allocator types.
*
* The newly-created %deque uses a copy of the allocation object used
* by @a x.
*/
deque(const deque& __x)
: _Base(__x.get_allocator(), __x.size())
{ std::uninitialized_copy(__x.begin(), __x.end(), this->_M_impl._M_start); }
/**
* @brief Builds a %deque from a range.
* @param first An input iterator.
* @param last An input iterator.
*
* Create a %deque consisting of copies of the elements from [first,
* last).
*
* If the iterators are forward, bidirectional, or random-access, then
* this will call the elements' copy constructor N times (where N is
* distance(first,last)) and do no memory reallocation. But if only
* input iterators are used, then this will do at most 2N calls to the
* copy constructor, and logN memory reallocations.
*/
template<typename _InputIterator>
deque(_InputIterator __first, _InputIterator __last,
const allocator_type& __a = allocator_type())
: _Base(__a)
{
// Check whether it's an integral type. If so, it's not an iterator.
typedef typename _Is_integer<_InputIterator>::_Integral _Integral;
_M_initialize_dispatch(__first, __last, _Integral());
}
/**
* The dtor only erases the elements, and note that if the elements
* themselves are pointers, the pointed-to memory is not touched in any
* way. Managing the pointer is the user's responsibilty.
*/
~deque()
{ std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish); }
/**
* @brief %Deque assignment operator.
* @param x A %deque of identical element and allocator types.
*
* All the elements of @a x are copied, but unlike the copy constructor,
* the allocator object is not copied.
*/
deque&
operator=(const deque& __x);
/**
* @brief Assigns a given value to a %deque.
* @param n Number of elements to be assigned.
* @param val Value to be assigned.
*
* This function fills a %deque with @a n copies of the given value.
* Note that the assignment completely changes the %deque and that the
* resulting %deque's size is the same as the number of elements assigned.
* Old data may be lost.
*/
void
assign(size_type __n, const value_type& __val)
{ _M_fill_assign(__n, __val); }
/**
* @brief Assigns a range to a %deque.
* @param first An input iterator.
* @param last An input iterator.
*
* This function fills a %deque with copies of the elements in the
* range [first,last).
*
* Note that the assignment completely changes the %deque and that the
* resulting %deque's size is the same as the number of elements
* assigned. Old data may be lost.
*/
template<typename _InputIterator>
void
assign(_InputIterator __first, _InputIterator __last)
{
typedef typename _Is_integer<_InputIterator>::_Integral _Integral;
_M_assign_dispatch(__first, __last, _Integral());
}
/// Get a copy of the memory allocation object.
allocator_type
get_allocator() const
{ return _Base::get_allocator(); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -