SGI STL的容器实现(traits技术广泛用于容器中和迭代器中)
ps:篇幅原因代码经过删减并非完整代码
1)vector容器
vector容器可以想象成一个动态数组,对于空间足够的情况,行为类似数组但更加安全,当空间不足时,vector容器会自动实现 "配置新空间/数据移动/释放旧空间" 的行为,vector容器的空间配置器默认使用的是上次讲到的alloc配置器,迭代器其实就是指针,嗯...除了要加上一些traits技术用到的型别定义整个实现还是比较简单的
template <class T, class Alloc = alloc>
class vector {
public:
typedef T value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type* iterator;
typedef const value_type* const_iterator;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
protected:
typedef simple_alloc<value_type, Alloc> data_allocator;
iterator start;
iterator finish;
iterator end_of_storage;
void insert_aux(iterator position, const T& x);
void deallocate() {
if (start) data_allocator::deallocate(start, end_of_storage - start);
}
void fill_initialize(size_type n, const T& value) {
start = allocate_and_fill(n, value);
finish = start + n;
end_of_storage = finish;
}
public:
iterator begin() { return start; }
const_iterator begin() const { return start; }
iterator end() { return finish; }
const_iterator end() const { return finish; }
size_type size() const { return size_type(end() - begin()); }
size_type max_size() const { return size_type(-1) / sizeof(T); }
size_type capacity() const { return size_type(end_of_storage - begin()); }
bool empty() const { return begin() == end(); }
reference operator[](size_type n) { return *(begin() + n); }
const_reference operator[](size_type n) const { return *(begin() + n); }
vector() : start(0), finish(0), end_of_storage(0) {}
vector(size_type n, const T& value) { fill_initialize(n, value); }
vector(int n, const T& value) { fill_initialize(n, value); }
vector(long n, const T& value) { fill_initialize(n, value); }
explicit vector(size_type n) { fill_initialize(n, T()); }
vector(const vector<T, Alloc>& x) {
start = allocate_and_copy(x.end() - x.begin(), x.begin(), x.end());
finish = start + (x.end() - x.begin());
end_of_storage = finish;
}
~vector() {
destroy(start, finish);
deallocate();
}
vector<T, Alloc>& operator=(const vector<T, Alloc>& x);
reference front() { return *begin(); }
const_reference front() const { return *begin(); }
reference back() { return *(end() - 1); }
const_reference back() const { return *(end() - 1); }
void push_back(const T& x) {
if (finish != end_of_storage) {
construct(finish, x);
++finish;
}
else
insert_aux(end(), x);
}
iterator insert(iterator position) { return insert(position, T()); }
void insert (iterator pos, size_type n, const T& x);
void pop_back() {
--finish;
destroy(finish);
}
iterator erase(iterator position) {
if (position + 1 != end())
copy(position + 1, finish, position);
--finish;
destroy(finish);
return position;
}
void resize(size_type new_size, const T& x) {
if (new_size < size())
erase(begin() + new_size, end());
else
insert(end(), new_size - size(), x);
}
void resize(size_type new_size) { resize(new_size, T()); }
void clear() { erase(begin(), end()); }
protected:
iterator allocate_and_fill(size_type n, const T& x) {
iterator result = data_allocator::allocate(n);
__STL_TRY {
uninitialized_fill_n(result, n, x);
return result;
}
__STL_UNWIND(data_allocator::deallocate(result, n));
}
};
大概定义也就这样,为了简便删了许多操作
2)list容器
list的底层实现其实就是个双向循环链表,并且只需要一个节点成员就够了(一个空的节点本身作为end(),end().next其实就是begin(),很多容器都是这么干的用一个空节点来简化操作在很多容器用到了,同时也是C++容器弥漫各处的半开闭区间的最好表现),空间配置器缺省也为那个alloc,节点结构就跟你要写一个双向循环链表的定义一样,但是为了获得类似迭代器的行为以适应各种操作、算法达到泛化的效果,list配备了一个自己专门的迭代器来包装list节点指针,其中list节点即为其数据成员,然后重载各种操作符达到模拟指针的各种操作,而实际的定义如下
template <class T, class Alloc = alloc>
class list {
protected:
typedef void* void_pointer;
typedef __list_node<T> list_node;
typedef simple_alloc<list_node, Alloc> list_node_allocator;
public:
typedef T value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef list_node* link_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
public:
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;
protected:
link_type get_node() { return list_node_allocator::allocate(); }
void put_node(link_type p) { list_node_allocator::deallocate(p); }
link_type create_node(const T& x) {
link_type p = get_node();
__STL_TRY {
construct(&p->data, x);
}
__STL_UNWIND(put_node(p));
return p;
}
void destroy_node(link_type p) {
destroy(&p->data);
put_node(p);
}
protected:
void empty_initialize() {
node = get_node();
node->next = node;
node->prev = node;
}
void fill_initialize(size_type n, const T& value) {
empty_initialize();
__STL_TRY {
insert(begin(), n, value);
}
__STL_UNWIND(clear(); put_node(node));
}
protected:
link_type node;
public:
list() { empty_initialize(); }
iterator begin() { return (link_type)((*node).next); }
const_iterator begin() const { return (link_type)((*node).next); }
iterator end() { return node; }
const_iterator end() const { return node; }
bool empty() const { return node->next == node; }
size_type size() const {
size_type result = 0;
distance(begin(), end(), result);
return result;
}
size_type max_size() const { return size_type(-1); }
reference front() { return *begin(); }
const_reference front() const { return *begin(); }
reference back() { return *(--end()); }
const_reference back() const { return *(--end()); }
void swap(list<T, Alloc>& x) { __STD::swap(node, x.node); }
iterator insert(iterator position, const T& x) {
link_type tmp = create_node(x);
tmp->next = position.node;
tmp->prev = position.node->prev;
(link_type(position.node->prev))->next = tmp;
position.node->prev = tmp;
return tmp;
}
iterator insert(iterator position) { return insert(position, T()); }
void insert(iterator pos, size_type n, const T& x);
void insert(iterator pos, int n, const T& x) {
insert(pos, (size_type)n, x);
}
void insert(iterator pos, long n, const T& x) {
insert(pos, (size_type)n, x);
}
void push_front(const T& x) { insert(begin(), x); }
void push_back(const T& x) { insert(end(), x); }
iterator erase(iterator position) {
link_type next_node = link_type(position.node->next);
link_type prev_node = link_type(position.node->prev);
prev_node->next = next_node;
next_node->prev = prev_node;
destroy_node(position.node);
return iterator(next_node);
}
iterator erase(iterator first, iterator last);
void resize(size_type new_size, const T& x);
void resize(size_type new_size) { resize(new_size, T()); }
void clear();
void pop_front() { erase(begin()); }
void pop_back() {
iterator tmp = end();
erase(--tmp);
}
list(size_type n, const T& value) { fill_initialize(n, value); }
list(int n, const T& value) { fill_initialize(n, value); }
list(long n, const T& value) { fill_initialize(n, value); }
explicit list(size_type n) { fill_initialize(n, T()); }
list(const list<T, Alloc>& x) {
range_initialize(x.begin(), x.end());
}
~list() {
clear();
put_node(node);
}
list<T, Alloc>& operator=(const list<T, Alloc>& x);
protected:
void transfer(iterator position, iterator first, iterator last) {
if (position != last) {
(*(link_type((*last.node).prev))).next = position.node;
(*(link_type((*first.node).prev))).next = last.node;
(*(link_type((*position.node).prev))).next = first.node;
link_type tmp = link_type((*position.node).prev);
(*position.node).prev = (*last.node).prev;
(*last.node).prev = (*first.node).prev;
(*first.node).prev = tmp;
}
}
public:
void splice(iterator position, list& x) {
if (!x.empty())
transfer(position, x.begin(), x.end());
}
void remove(const T& value);
void unique();
void merge(list& x);
void reverse();
void sort();
friend bool operator== __STL_NULL_TMPL_ARGS (const list& x, const list& y);
};
可以看到list中数据成员直接就是list的节点而不是经过包装后的迭代器,而且后面容器基本都是这么干,大概可以想象STL的设计者需要的并不是迭代器与容器之间的一种包含关系(如果是我的话我肯定这么干,直接包装节点,再把节点包装成迭代器,再把迭代器包装进对应的容器(层层包装这就是我对面向对象的认识了...看来没那么简单)。对于这种设计思路我反正是不懂为什么,能带来什么好处.....
按我自己的理解,就是让迭代器起到一个胶水的作用-----具体的容器实现相关的操作什么的,而STL的算法提供的就是一个迭代器接口,为了将2者既数据与算法粘合在一起,迭代器就产生了,他是一个独立于数据与算法的泛化类型而又连接起数据与算法,而容器为了产生迭代器,只要对自己的迭代器进行声明,当用户需要迭代器的时候容器只需要返回相关节点让迭代器以这个节点生成对应的迭代器并反馈给用户,而对于节点的操作由具体迭代器内部管理,算法因此得以行进。
以前一直认为迭代器只是在容器内对于指针的包装,现在看来,迭代器只是一个抽象,不属于任何谁,算法和数据(容器)是2个独立的板块,迭代器就是一个桥架起了算法与数据,让他们协同并高效运作。
ps:其实还有个不在C++标准内但SGI STL提供了的容器名为slist,slist和list的差别在于slist的迭代器是个前向迭代器而list的迭代器属于双向迭代器,并且slist的架构比list的架构复杂的多,slist本身是个单向链表,并且也准备了一个空节点以省去一些特殊情况操作,在这里是一个头结点。
3)deque容器极其配接器
对于deque容器,其内部实现也是线性的,但是deque容器是以动态地分段连续空间组合而成,随时可以增加一段新的空间并连接起来,而且元素的存储都是经过计算尽量放在中间段空间的,所以如果说vector最大的弊端就是对于头部元素的删除耗费大量时间以及空间,那么deque最擅长的就是头尾元素的操作,而且不会发生vector中由于空间不足而产生的内存重新分配,数据复制,释放旧空间的情况了.....嗯,也就是个双端队列了
不过deque因为他的结构,他的迭代器就不是普通的指针了,运算需要涉及跳跃段等操作,排序等操作因此就更复杂了,所以选用哪种容器需要斟酌。
实现中deque会负责维护一个指针数组,在STL源码剖析中叫中控器,数组中每个指针只想一个存储真正数据的缓冲区。
由于其特别的存储结构,deque容器内就需要2个空间配置器,一个是对于中控器也就是那个指针数组,还有一个就是常见的负责对元素的配置了,然后为了维护其结构的完整性,在插入删除元素等操作中都要进行情况测试以及可能的空间配置,所以对于大量的元素移动操作效率可想而知,下面附上定义
ps:看了下,迭代器在deque中就直接作为成员了...于是就有点搞不懂了
template <class T, class Alloc = alloc, size_t BufSiz = 0>
class deque {
public: // Basic types
typedef T value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
public: // Iterators
//…
protected: // Internal typedefs
typedef pointer* map_pointer;
typedef simple_alloc<value_type, Alloc> data_allocator;
typedef simple_alloc<pointer, Alloc> map_allocator;
static size_type buffer_size() {
return __deque_buf_size(BufSiz, sizeof(value_type));
}
static size_type initial_map_size() { return 8; }
protected: // Data members
iterator start;
iterator finish;
map_pointer map;
size_type map_size;
public: // Basic accessors
iterator begin() { return start; }
iterator end() { return finish; }
const_iterator begin() const { return start; }
const_iterator end() const { return finish; }
reverse_iterator rbegin() { return reverse_iterator(finish); }
reverse_iterator rend() { return reverse_iterator(start); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(finish);
}
const_reverse_iterator rend() const {
return const_reverse_iterator(start);
}
reference operator[](size_type n) { return start[difference_type(n)]; }
const_reference operator[](size_type n) const {
return start[difference_type(n)];
}
reference front() { return *start; }
reference back() {
iterator tmp = finish;
--tmp;
return *tmp;
}
const_reference front() const { return *start; }
const_reference back() const {
const_iterator tmp = finish;
--tmp;
return *tmp;
}
size_type size() const { return finish - start;; }
size_type max_size() const { return size_type(-1); }
bool empty() const { return finish == start; }
public: // Constructor, destructor.
deque()
: start(), finish(), map(0), map_size(0)
{
create_map_and_nodes(0);
}
deque(const deque& x)
: start(), finish(), map(0), map_size(0)
{
create_map_and_nodes(x.size());
__STL_TRY {
uninitialized_copy(x.begin(), x.end(), start);
}
__STL_UNWIND(destroy_map_and_nodes());
}
deque(size_type n, const value_type& value)
: start(), finish(), map(0), map_size(0)
{
fill_initialize(n, value);
}
explicit deque(size_type n)
: start(), finish(), map(0), map_size(0)
{
fill_initialize(n, value_type());
}
~deque() {
destroy(start, finish);
destroy_map_and_nodes();
}
deque& operator= (const deque& x) {
const size_type len = size();
if (&x != this) {
if (len >= x.size())
erase(copy(x.begin(), x.end(), start), finish);
else {
const_iterator mid = x.begin() + difference_type(len);
copy(x.begin(), mid, start);
insert(finish, mid, x.end());
}
}
return *this;
}
public: // push_* and pop_*
//..
public: // Insert
iterator insert(iterator position, const value_type& x) {
if (position.cur == start.cur) {
push_front(x);
return start;
}
else if (position.cur == finish.cur) {
push_back(x);
iterator tmp = finish;
--tmp;
return tmp;
}
else {
return insert_aux(position, x);
}
}
iterator insert(iterator position) { return insert(position, value_type()); }
void insert(iterator pos, size_type n, const value_type& x);
void insert(iterator pos, int n, const value_type& x) {
insert(pos, (size_type) n, x);
}
void insert(iterator pos, long n, const value_type& x) {
insert(pos, (size_type) n, x);
}
void resize(size_type new_size, const value_type& x) {
const size_type len = size();
if (new_size < len)
erase(start + new_size, finish);
else
insert(finish, new_size - len, x);
}
void resize(size_type new_size) { resize(new_size, value_type()); }
public: // Erase
iterator erase(iterator pos) {
iterator next = pos;
++next;
difference_type index = pos - start;
if (index < (size() >> 1)) {
copy_backward(start, pos, next);
pop_front();
}
else {
copy(next, finish, pos);
pop_back();
}
return start + index;
}
iterator erase(iterator first, iterator last);
void clear();
protected: // Internal construction/destruction
void create_map_and_nodes(size_type num_elements);
void destroy_map_and_nodes();
void fill_initialize(size_type n, const value_type& value);
protected: // Internal push_* and pop_*
void push_back_aux(const value_type& t);
void push_front_aux(const value_type& t);
void pop_back_aux();
void pop_front_aux();
protected: // Internal insert functions
iterator insert_aux(iterator pos, const value_type& x);
void insert_aux(iterator pos, size_type n, const value_type& x);
void new_elements_at_front(size_type new_elements);
void new_elements_at_back(size_type new_elements);
void destroy_nodes_at_front(iterator before_start);
void destroy_nodes_at_back(iterator after_finish);
protected: // Allocation of map and nodes
// Makes sure the map has space for new nodes. Does not actually
// add the nodes. Can invalidate map pointers. (And consequently,
// deque iterators.)
void reallocate_map(size_type nodes_to_add, bool add_at_front);
pointer allocate_node() { return data_allocator::allocate(buffer_size()); }
void deallocate_node(pointer n) {
data_allocator::deallocate(n, buffer_size());
}
};
然后是deque的配接器stack和queue了,有了deque这2个东西就很好搞了,只要将deque作为其成员然后只提供一些操作就能模拟出这2种数据结构了
4)priority_queue容器
其实就是个优先级队列,底层是一个heap结构,由于heap结构的原因,他并不提供迭代器,而priority_queue只需将heap作为成员,提供一些转调用操作即可实现priority_queue容器,代码就不上了
5)set,map等关联容器
这几个关联容器底层都是以红黑树实现,至于具体的相关红黑树实现可以到STL源码剖析或者数据结构方面的书上找,并且STL中实现提供了一个比根节点还要高一点的头结点来实现一些技巧并避免一些特殊情况的处理,他的迭代器结构也比较复杂...下面直接上个set的定义吧
template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:
// typedefs:
typedef Key key_type;
typedef Key value_type;
typedef Compare key_compare;
typedef Compare value_compare;
private:
typedef rb_tree<key_type, value_type,
identity<value_type>, key_compare, Alloc> rep_type;
rep_type t; // red-black tree representing set
public:
typedef typename rep_type::const_pointer pointer;
typedef typename rep_type::const_pointer const_pointer;
typedef typename rep_type::const_reference reference;
typedef typename rep_type::const_reference const_reference;
typedef typename rep_type::const_iterator iterator;
typedef typename rep_type::const_iterator const_iterator;
typedef typename rep_type::const_reverse_iterator reverse_iterator;
typedef typename rep_type::const_reverse_iterator const_reverse_iterator;
typedef typename rep_type::size_type size_type;
typedef typename rep_type::difference_type difference_type;
// allocation/deallocation
set() : t(Compare()) {}
explicit set(const Compare& comp) : t(comp) {}
#ifdef __STL_MEMBER_TEMPLATES
template <class InputIterator>
set(InputIterator first, InputIterator last)
: t(Compare()) { t.insert_unique(first, last); }
template <class InputIterator>
set(InputIterator first, InputIterator last, const Compare& comp)
: t(comp) { t.insert_unique(first, last); }
#else
set(const value_type* first, const value_type* last)
: t(Compare()) { t.insert_unique(first, last); }
set(const value_type* first, const value_type* last, const Compare& comp)
: t(comp) { t.insert_unique(first, last); }
set(const_iterator first, const_iterator last)
: t(Compare()) { t.insert_unique(first, last); }
set(const_iterator first, const_iterator last, const Compare& comp)
: t(comp) { t.insert_unique(first, last); }
#endif /* __STL_MEMBER_TEMPLATES */
set(const set<Key, Compare, Alloc>& x) : t(x.t) {}
set<Key, Compare, Alloc>& operator=(const set<Key, Compare, Alloc>& x) {
t = x.t;
return *this;
}
// accessors:
key_compare key_comp() const { return t.key_comp(); }
value_compare value_comp() const { return t.key_comp(); }
iterator begin() const { return t.begin(); }
iterator end() const { return t.end(); }
reverse_iterator rbegin() const { return t.rbegin(); }
reverse_iterator rend() const { return t.rend(); }
bool empty() const { return t.empty(); }
size_type size() const { return t.size(); }
size_type max_size() const { return t.max_size(); }
void swap(set<Key, Compare, Alloc>& x) { t.swap(x.t); }
// insert/erase
typedef pair<iterator, bool> pair_iterator_bool;
pair<iterator,bool> insert(const value_type& x) {
pair<typename rep_type::iterator, bool> p = t.insert_unique(x);
return pair<iterator, bool>(p.first, p.second);
}
iterator insert(iterator position, const value_type& x) {
typedef typename rep_type::iterator rep_iterator;
return t.insert_unique((rep_iterator&)position, x);
}
#ifdef __STL_MEMBER_TEMPLATES
template <class InputIterator>
void insert(InputIterator first, InputIterator last) {
t.insert_unique(first, last);
}
#else
void insert(const_iterator first, const_iterator last) {
t.insert_unique(first, last);
}
void insert(const value_type* first, const value_type* last) {
t.insert_unique(first, last);
}
#endif /* __STL_MEMBER_TEMPLATES */
void erase(iterator position) {
typedef typename rep_type::iterator rep_iterator;
t.erase((rep_iterator&)position);
}
size_type erase(const key_type& x) {
return t.erase(x);
}
void erase(iterator first, iterator last) {
typedef typename rep_type::iterator rep_iterator;
t.erase((rep_iterator&)first, (rep_iterator&)last);
}
void clear() { t.clear(); }
// set operations:
iterator find(const key_type& x) const { return t.find(x); }
size_type count(const key_type& x) const { return t.count(x); }
iterator lower_bound(const key_type& x) const {
return t.lower_bound(x);
}
iterator upper_bound(const key_type& x) const {
return t.upper_bound(x);
}
pair<iterator,iterator> equal_range(const key_type& x) const {
return t.equal_range(x);
}
friend bool operator== __STL_NULL_TMPL_ARGS (const set&, const set&);
friend bool operator< __STL_NULL_TMPL_ARGS (const set&, const set&);
};
可以看到红黑树作为set的一个成员,并且调用基本都是转调用到红黑树中的实现,至于map ,multiset,multimap大同小异,都是这么实现的,就不多说
6)hash_set等容器
顾名思义,他们和set,map等行为差不多,只是底层实现以hash表实现,所以查找删除插入是他们的强项,具有常熟平均时间。
hash table的实现有很多种方法,线性探测法,二次探测法,开链法。至于hash_set等的底层hash table实现,则属于这种开链法
也就是准备一定数量的桶子,每个桶子指向一个链表,每个元素直接插入hash后得到的桶子中的链表中,也就省去了碰撞后的搜寻,下面贴上这种hash_table的实现
template <class Value>
struct __hashtable_node//链表节点
{
__hashtable_node* next;
Value val;
};
template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey, class Alloc = alloc>
class hashtable;
template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey, class Alloc>
struct __hashtable_iterator;
template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey, class Alloc>
struct __hashtable_const_iterator;
template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey, class Alloc>
struct __hashtable_iterator { //hash表迭代器
typedef hashtable<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>
hashtable;
typedef __hashtable_iterator<Value, Key, HashFcn,
ExtractKey, EqualKey, Alloc>
iterator;
typedef __hashtable_const_iterator<Value, Key, HashFcn,
ExtractKey, EqualKey, Alloc>
const_iterator;
typedef __hashtable_node<Value> node;
typedef forward_iterator_tag iterator_category;
typedef Value value_type;
typedef ptrdiff_t difference_type;
typedef size_t size_type;
typedef Value& reference;
typedef Value* pointer;
node* cur;
hashtable* ht;
__hashtable_iterator(node* n, hashtable* tab) : cur(n), ht(tab) {}
__hashtable_iterator() {}
reference operator*() const { return cur->val; }
#ifndef __SGI_STL_NO_ARROW_OPERATOR
pointer operator->() const { return &(operator*()); }
#endif /* __SGI_STL_NO_ARROW_OPERATOR */
iterator& operator++();
iterator operator++(int);
bool operator==(const iterator& it) const { return cur == it.cur; }
bool operator!=(const iterator& it) const { return cur != it.cur; }
};
static const int __stl_num_primes = 28; //质数个数
static const unsigned long __stl_prime_list[__stl_num_primes] = //质数数组,既桶子数
{
53, 97, 193, 389, 769,
1543, 3079, 6151, 12289, 24593,
49157, 98317, 196613, 393241, 786433,
1572869, 3145739, 6291469, 12582917, 25165843,
50331653, 100663319, 201326611, 402653189, 805306457,
1610612741, 3221225473ul, 4294967291ul
};
inline unsigned long __stl_next_prime(unsigned long n) //全剧函数,获得下一个质数大小
{
const unsigned long* first = __stl_prime_list;
const unsigned long* last = __stl_prime_list + __stl_num_primes;
const unsigned long* pos = lower_bound(first, last, n);
return pos == last ? *(last - 1) : *pos;
}
template <class Value, class Key, class HashFcn,
class ExtractKey, class EqualKey,
class Alloc>
class hashtable {
public:
typedef Key key_type;
typedef Value value_type;
typedef HashFcn hasher;
typedef EqualKey key_equal;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
hasher hash_funct() const { return hash; }
key_equal key_eq() const { return equals; }
private:
hasher hash;
key_equal equals;
ExtractKey get_key;
typedef __hashtable_node<Value> node;
typedef simple_alloc<node, Alloc> node_allocator;
vector<node*,Alloc> buckets;
size_type num_elements;
public:
typedef __hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey,
Alloc>
iterator;
typedef __hashtable_const_iterator<Value, Key, HashFcn, ExtractKey, EqualKey,
Alloc>
const_iterator;
friend struct
__hashtable_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>;
friend struct
__hashtable_const_iterator<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>;
public:
hashtable(size_type n,
const HashFcn& hf,
const EqualKey& eql,
const ExtractKey& ext)
: hash(hf), equals(eql), get_key(ext), num_elements(0)
{
initialize_buckets(n);
}
hashtable(size_type n,
const HashFcn& hf,
const EqualKey& eql)
: hash(hf), equals(eql), get_key(ExtractKey()), num_elements(0)
{
initialize_buckets(n);
}
hashtable(const hashtable& ht)
: hash(ht.hash), equals(ht.equals), get_key(ht.get_key), num_elements(0)
{
copy_from(ht);
}
hashtable& operator= (const hashtable& ht)
{
if (&ht != this) {
clear();
hash = ht.hash;
equals = ht.equals;
get_key = ht.get_key;
copy_from(ht);
}
return *this;
}
~hashtable() { clear(); }
size_type size() const { return num_elements; }
size_type max_size() const { return size_type(-1); }
bool empty() const { return size() == 0; }
void swap(hashtable& ht)
{
__STD::swap(hash, ht.hash);
__STD::swap(equals, ht.equals);
__STD::swap(get_key, ht.get_key);
buckets.swap(ht.buckets);
__STD::swap(num_elements, ht.num_elements);
}
iterator begin()
{
for (size_type n = 0; n < buckets.size(); ++n)
if (buckets[n])
return iterator(buckets[n], this);
return end();
}
iterator end() { return iterator(0, this); }
const_iterator begin() const
{
for (size_type n = 0; n < buckets.size(); ++n)
if (buckets[n])
return const_iterator(buckets[n], this);
return end();
}
const_iterator end() const { return const_iterator(0, this); }
friend bool
operator== __STL_NULL_TMPL_ARGS (const hashtable&, const hashtable&);
public:
size_type bucket_count() const { return buckets.size(); }
size_type max_bucket_count() const
{ return __stl_prime_list[__stl_num_primes - 1]; }
size_type elems_in_bucket(size_type bucket) const
{
size_type result = 0;
for (node* cur = buckets[bucket]; cur; cur = cur->next)
result += 1;
return result;
}
pair<iterator, bool> insert_unique(const value_type& obj)
{
resize(num_elements + 1);
return insert_unique_noresize(obj);
}
iterator insert_equal(const value_type& obj)
{
resize(num_elements + 1);
return insert_equal_noresize(obj);
}
pair<iterator, bool> insert_unique_noresize(const value_type& obj);
iterator insert_equal_noresize(const value_type& obj);
#ifdef __STL_MEMBER_TEMPLATES
template <class InputIterator>
void insert_unique(InputIterator f, InputIterator l)
{
insert_unique(f, l, iterator_category(f));
}
template <class InputIterator>
void insert_equal(InputIterator f, InputIterator l)
{
insert_equal(f, l, iterator_category(f));
}
template <class InputIterator>
void insert_unique(InputIterator f, InputIterator l,
input_iterator_tag)
{
for ( ; f != l; ++f)
insert_unique(*f);
}
template <class InputIterator>
void insert_equal(InputIterator f, InputIterator l,
input_iterator_tag)
{
for ( ; f != l; ++f)
insert_equal(*f);
}
template <class ForwardIterator>
void insert_unique(ForwardIterator f, ForwardIterator l,
forward_iterator_tag)
{
size_type n = 0;
distance(f, l, n);
resize(num_elements + n);
for ( ; n > 0; --n, ++f)
insert_unique_noresize(*f);
}
template <class ForwardIterator>
void insert_equal(ForwardIterator f, ForwardIterator l,
forward_iterator_tag)
{
size_type n = 0;
distance(f, l, n);
resize(num_elements + n);
for ( ; n > 0; --n, ++f)
insert_equal_noresize(*f);
}
#else /* __STL_MEMBER_TEMPLATES */
void insert_unique(const value_type* f, const value_type* l)
{
size_type n = l - f;
resize(num_elements + n);
for ( ; n > 0; --n, ++f)
insert_unique_noresize(*f);
}
void insert_equal(const value_type* f, const value_type* l)
{
size_type n = l - f;
resize(num_elements + n);
for ( ; n > 0; --n, ++f)
insert_equal_noresize(*f);
}
void insert_unique(const_iterator f, const_iterator l)
{
size_type n = 0;
distance(f, l, n);
resize(num_elements + n);
for ( ; n > 0; --n, ++f)
insert_unique_noresize(*f);
}
void insert_equal(const_iterator f, const_iterator l)
{
size_type n = 0;
distance(f, l, n);
resize(num_elements + n);
for ( ; n > 0; --n, ++f)
insert_equal_noresize(*f);
}
#endif /*__STL_MEMBER_TEMPLATES */
reference find_or_insert(const value_type& obj);
iterator find(const key_type& key)
{
size_type n = bkt_num_key(key);
node* first;
for ( first = buckets[n];
first && !equals(get_key(first->val), key);
first = first->next)
{}
return iterator(first, this);
}
const_iterator find(const key_type& key) const
{
size_type n = bkt_num_key(key);
const node* first;
for ( first = buckets[n];
first && !equals(get_key(first->val), key);
first = first->next)
{}
return const_iterator(first, this);
}
size_type count(const key_type& key) const
{
const size_type n = bkt_num_key(key);
size_type result = 0;
for (const node* cur = buckets[n]; cur; cur = cur->next)
if (equals(get_key(cur->val), key))
++result;
return result;
}
pair<iterator, iterator> equal_range(const key_type& key);
pair<const_iterator, const_iterator> equal_range(const key_type& key) const;
size_type erase(const key_type& key);
void erase(const iterator& it);
void erase(iterator first, iterator last);
void erase(const const_iterator& it);
void erase(const_iterator first, const_iterator last);
void resize(size_type num_elements_hint);
void clear();
private:
size_type next_size(size_type n) const { return __stl_next_prime(n); }
void initialize_buckets(size_type n)
{
const size_type n_buckets = next_size(n);
buckets.reserve(n_buckets);
buckets.insert(buckets.end(), n_buckets, (node*) 0);
num_elements = 0;
}
size_type bkt_num_key(const key_type& key) const
{
return bkt_num_key(key, buckets.size());
}
size_type bkt_num(const value_type& obj) const
{
return bkt_num_key(get_key(obj));
}
size_type bkt_num_key(const key_type& key, size_t n) const
{
return hash(key) % n;
}
size_type bkt_num(const value_type& obj, size_t n) const
{
return bkt_num_key(get_key(obj), n);
}
node* new_node(const value_type& obj)
{
node* n = node_allocator::allocate();
n->next = 0;
__STL_TRY {
construct(&n->val, obj);
return n;
}
__STL_UNWIND(node_allocator::deallocate(n));
}
void delete_node(node* n)
{
destroy(&n->val);
node_allocator::deallocate(n);
}
void erase_bucket(const size_type n, node* first, node* last);
void erase_bucket(const size_type n, node* last);
void copy_from(const hashtable& ht);
};
其中涉及到重建表格的判断值得一提,判断表格是否需要重建的原则是拿总元素个数和桶子数比较,桶子用vector容器代替,如果前者大于后者,就重建表格,这样判断肯定有一定的理由。我觉得如果经过一番严密的数学计算的话这种判断重建应该是可以控制插入删除等操作的时间复杂度在一定范围内,以达到高效的目的而不会让一条链很长。
然后hash_set等容器就是把hash_table作为一个成员类似set等容器直接转调用就OK了。
ps:看了这些个STL源码发现很多代码分成多个函数宁愿来转调用也不把多个函数组成一个函数,而且前几天反汇编过过一个转调用发现在汇编里所谓的转调用已经不用call跳转了,相信这都是编译器的功劳。所以相信编译器大胆的去转调用吧(这。。),事实上很多看起来提升效率的做法不仅没有提升效率反而降低了可读性、封装性等,所以就不要吝啬一个个调用吧,编译器的强大超乎想象。。。。