六大部件
迭代器iterator是一种泛化指针,Adapter用于做转换,仿函数用于实现加减等简单的功能。
六大部件使用例子
#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>
using namespace std;
int main()
{
int ia[6] = {1, 2, 3, 4, 5, 6};
vector<int, allocator<int>> vi(ia, ia + 6);
cout << count_if(vi.begin(), vi.end(),
not1(bind2nd(less<int>(), 3)));
//not1是一个function adapter(negator)
//bind2nd是一个function adapter(binder)
//less<int>()是一个function object(是个临时对象)
return 0;
}
c.begin()和c.end()传回的是iterator对象,即元素指针。分别指向第一个元素和最后一个元素的下一个元素,元素区间为前闭后开,可以使用*(c.begin()),但不能用 *(c.end())。
调用方式:
Container<T> c;
...
Container<T>::iterator ite = c.begin();
for(; ite != c.end(); ++ite)
...
C++11后
std::vector<douuble> vec;
...
for (auto elem: vec) {
std::cout<<elem<<std::endl;
}
for (auto& elem : vec) {
elem *= 3;
}
//initializer_list
for ( int i : {1, 2, 3, 4}){
std::cout << i << std::endl;
}
容器分类与测试
大致分为两类:序列型容器和关联型容器,标红的是C++11后出现的新容器
测试序列式容器
测试的辅助函数
定义两个target输入函数辅助容器的find函数查找元素,定义两个比较准则来比较容器元素。
(1)array
(2)vector
- push_back()了一百万个元素,vector用了3000ms,和array比起来慢了很多,原因是vector需要二倍成长扩容。vector每次二倍成长时都要开辟新的空间,将原来的vector拷贝到新的空间内,而array的空间是静态分配的。
- vector使用::find几乎不耗时,反而是二分查找时间较长,运气比较好
(3)list
(4)forward_list
单链表没有size()和back()的成员函数
(5)deque
deque的两端扩容
对比vector的扩容是在一个地方开辟足够的空间,然后把原来的地方的元素copy过去,因为原地扩容的话可能地方会不够用,而deque不是采用这种方式扩容。
deque是由一段段buffer组成的,每段buffer有固定的长度,deque维护指向每个buffer的指针,当deque需要扩容时,就在前面或后面新增一个buffer。因此实质上deque是分段连续的,但在deque源码中通过重载迭代器的相关操作使其表现为整段连续。
deque测试情况
容器适配器stack和queue内部就是一个deque,从而实现栈和普通队列的行为
测试关联容器
(1)multiset
容器本身的find()比全局的find()快很多,这是因为multiset内部采用红黑树结构实现,查找速度为log2n
(2)multimap
插入时用pair<>将两个key和value元素配对后再插入,multimap不能用c[ key ] = value 即用[ ]做插入。其内部也采用红黑树实现,可进行元素的快速查找。
(3)unordered_multiset
其中有一个bucket_count()的成员函数,可以看见篮子的数量,内部采用hashtable实现
(4)unordered_multimap
同样不可以采用[ ]做插入
(5)set,map,unorded_set,unordered_map
实现和上面大同小异,但要注意的一点是map和unordered_map可以用[ ]做插入,即
snprintf(buf, 10, "%d", rand());
c[i] = string(buf);
OOP和GP
- GP将datas和method分开来
- OOP将datas和method关联到一起
在list中体现了OOP,list不能使用全局的::sort()函数
我们发现::sort()支持的迭代器是随机访问迭代器,随机访问迭代器除了可以递增递减之外,还支持类似c.begin() + k的操作,而list中的迭代器不是随机访问迭代器,所以也就不提供这个操作。这样设计是合理的,因为list是链表,无法在线性时间内得到递增或递减k次的结果。
在其他容器如vector和deque中,可以调用全局的sort函数进行排序,其中_Compare _compare为仿函数对象,用于比较元素大小,定义如下:
图中调用了strLonger仿函数的比较准则来进行元素大小的比较,返回一个bool值
采用GP优势
- Container和Algorithms可以各自闭门造成,用iterator沟通即可
- Algorithms通过Iterators确定范围,并通过Itertors确定操作范围,并通过Iterators操作Container元素
操作符重载
有些操作符重载既可以是成员函数也可以使全局函数,有些操作符重载只能是成员函数
例子
template <class T, class Ref, class Ptr>
struct __list_iterator{
typedef __list_iterator<T, Ref, Ptr> self;
typedef bidirectional_iterator_tag iterator_catagory;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef __list_node<T>* link_type;
typedef ptrdiff_t difference_type;
link_type node;
reference operator*() const { return (*node).data; }
pointer operator->() const { return &(operator*()); }
self& operator++() { node = (link_type)((*node).next); return *this; }
self operator++(int) { self tmp = *this; ++*this; return tmp; }
};
模板
偏特化模板
(1)个数上偏
//泛化
template <class T,
class Alloc = alloc>
class vector
{
......
};
//个数上的偏特化
template <class Alloc>
class vector<bool, Alloc>
{
......
};
范围上偏
//泛化
template <class Iterator>
struct iterator_traits{
typedef typename Iterator::iterator_catagory iterator_catagory;
typedef typename Iterator::value_type value_type;
typedef typename Iterator::difference_type difference_type;
typedef typename Iterator::pointer pointer;
typedef typename Iterator::reference reference;
};
//partial specialization for regular pointers
template <class T>
struct iterator_traits<T*>{
typedef random_access_iterator_tag iterator_catagory;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
};
//partial specialization for regular const pointers
template <class T>
struct iterator_traits<const T*>{
typedef random_access_iterator_tag iterator_catagory;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
};