map 是一种关联式容器,它的所有元素都是 pair,pair 的第一元素为键值 key,第二元素为实值 value,不允许两个元素有相同的键值,且所有元素都会根据元素的键值自动排序。下面从源码角度看看 map 是如何实现的,假设有这么段代码:
map<int, int> testMap;
testMap[1] = 2;
testMap[2] = 4;
testMap[3] = 6;
代码首先定义了个键值和实值都为 int 类型的 testMap,先看下 map 的定义:
template <class _Key, class _Tp, class _Compare = less<_Key>,
class _Allocator = allocator<pair<const _Key, _Tp> > >
class _LIBCPP_TEMPLATE_VIS map
{
public:
// types:
typedef _Key key_type;
typedef _Tp mapped_type;
typedef pair<const key_type, mapped_type> value_type;
...
private:
...
__base __tree_;
...
}
此处 __base_ 类型定义暂时省略,可以看出以下几点:
1. 模板类的第三个参数 _Compare 默认采用键值的递增排序;
2. 分配器的参数是个 pair,第一个参数是经 const 修饰的键值,第二个是对应的实值;
3. 有个私有类型 __tree_,通过此成员与红黑树关联。
testMap 定义好后,通过 [] 运算符添加了三个元素,这里 map 对 operator[] 有进行重载:
template <class _Key, class _Tp, class _Compare, class _Allocator>
_Tp&
map<_Key, _Tp, _Compare, _Allocator>::operator[](const key_type& __k)
{
return __tree_.__emplace_unique_key_args(__k,
_VSTD::piecewise_construct,
_VSTD::forward_as_tuple(__k),
_VSTD::forward_as_tuple()).first->__get_value().second;
}
看方法体也只是转调用了红黑树的 __emplace_unique_key_args() 方法:
template <class _Tp, class _Compare, class _Allocator>
template <class _Key, class... _Args>
pair<typename __tree<_Tp, _Compare, _Allocator>::iterator, bool>
__tree<_Tp, _Compare, _Allocator>::__emplace_unique_key_args(_Key const& __k, _Args&&... __args)
{
__parent_pointer __parent;
__node_base_pointer& __child = __find_equal(__parent, __k);
__node_pointer __r = static_cast<__node_pointer>(__child);
bool __inserted = false;
if (__child == nullptr)
{
__node_holder __h = __construct_node(_VSTD::forward<_Args>(__args)...);
__insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
__r = __h.release();
__inserted = true;
}
return pair<iterator, bool>(iterator(__r), __inserted);
}
该方法返回的是个 pair,第一个元素是迭代器,第二个元素为 bool 类型,若为真则表示插入成功,反之插入失败。方法体中首先调用 __find_equal_() 方法判断当前插入的元素键值是否已存在,若存在则 __child 为空,且将 bool 型设为 false;若不存在则分别调用 __construct_node() 方法创建节点和 __insert_node_at() 方法插入节点。
template <class _Tp, class _Compare, class _Allocator>
void __tree<_Tp, _Compare, _Allocator>::__insert_node_at(
__parent_pointer __parent, __node_base_pointer& __child,
__node_base_pointer __new_node) _NOEXCEPT
{
__new_node->__left_ = nullptr;
__new_node->__right_ = nullptr;
__new_node->__parent_ = __parent;
// __new_node->__is_black_ is initialized in __tree_balance_after_insert
__child = __new_node;
if (__begin_node()->__left_ != nullptr)
__begin_node() = static_cast<__iter_pointer>(__begin_node()->__left_);
_VSTD::__tree_balance_after_insert(__end_node()->__left_, __child);
++size();
}
__insert_node_at() 方法实际上不仅仅只是插入节点,插入后还会调用方法 __tree_balance_after_insert() 进行再平衡,以符合红黑树的内在要求。元素添加以后可对其遍历:
map<int, int>::iterator it;
for (it = testMap.begin(); it != testMap.end(); ++it) {
cout << "key: " << it->first << ", value: " << it->second << endl;
}
这里先是定义了个迭代器,并将初始值赋为 testMap.begin(),然后依次遍历。那么迭代器是怎么和 pair 关联起来的呢?看下 begin() 方法实现:
// map
typedef __map_iterator<typename __base::iterator> iterator;
iterator begin() _NOEXCEPT {return __tree_.begin();}
// __tree
typedef __tree_iterator<value_type, __node_pointer, difference_type> iterator;
iterator begin() _NOEXCEPT {return iterator(__begin_node());}
__iter_pointer& __begin_node() _NOEXCEPT {return __begin_node_;}
__begin_node_ 是类 __tree_ 的一个 __iter_pointer 类型的私有成员,在给迭代器赋初始值过程中由 __begin_node() 方法包装返回,它始终指向红黑树中的最左节点,也就是键值的最小值节点,这点可以在上文提到的 __insert_node_at() 方法中看到。
另外 map 文件和 __tree 文件中的 begin() 方法返回的虽然都是 iterator 类型,但根据类型定义其实是不同的。那这俩又是怎么关联起来的呢?可以看下类模板 __map_iterator 定义:
template <class _TreeIterator>
class _LIBCPP_TEMPLATE_VIS __map_iterator
{
typedef typename _TreeIterator::_NodeTypes _NodeTypes;
typedef typename _TreeIterator::__pointer_traits __pointer_traits;
_TreeIterator __i_;
...
}
它有个私有成员 __i_,实际上就是 __tree_iterator 类型,此处可通过 gdb 调试来进行验证。
现在我们知道迭代器实际上就是个 __map_iterator 类型的对象,而这个对象可直接调用 pair 的数据成员 first 和 second,这里就要看声明 __map_iterator 对象时的 __base 的定义了。
// map
public:
// types:
typedef _Key key_type;
typedef _Tp mapped_type;
private:
typedef _VSTD::__value_type<key_type, mapped_type> __value_type;
typedef __map_value_compare<key_type, __value_type, key_compare> __vc;
typedef typename __rebind_alloc_helper<allocator_traits<allocator_type>, __value_type>::type __allocator_type;
typedef __tree<__value_type, __vc, __allocator_type> __base;
其中类 _VSTD::_value_type 定义如下:
template <class _Key, class _Tp>
struct __value_type
{
typedef _Key key_type;
typedef _Tp mapped_type;
typedef pair<const key_type, mapped_type> value_type;
typedef pair<key_type&, mapped_type&> __nc_ref_pair_type;
typedef pair<key_type&&, mapped_type&&> __nc_rref_pair_type;
private:
value_type __cc;
...
}
类 __value_type 有个 value_type 类型的私有成员 __cc,实际上也就是 pair 类型,至此就可以将迭代器 __map_iterator 与 pair 关联起来了,而 pair 定义如下:
template <class _T1, class _T2>
struct _LIBCPP_TEMPLATE_VIS pair
#if defined(_LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR)
: private __non_trivially_copyable_base<_T1, _T2>
#endif
{
typedef _T1 first_type;
typedef _T2 second_type;
_T1 first;
_T2 second;
...
explicit pair(_T1 const& __t1, _T2 const& __t2)
_NOEXCEPT_(is_nothrow_copy_constructible<first_type>::value &&
is_nothrow_copy_constructible<second_type>::value)
: first(__t1), second(__t2) {}
...
}
在构造函数中将模板参数 _t1 和 _t2 分别赋值给数据成员 first 和 second,自然也就可以通过它俩分别访问元素的键值和实值了。