C++泛型编程,及STL源码入门
速刷知识点,可用来查漏补缺
泛型编程
c++模板类分为class template 和 function template。
特化分为特例化(全特化)和部分特例化(偏特化)
- 模板和特例化版本应该声明在同一头文件,所有同名模板的声明应该放在前面,接着是特例化版本。
- 一个模板全特化的条件:(1)必须有一个主模板类 (2)模板类型全部明确化。
模板函数
template<typename T1, typename T2> void fun(T1 a, T2 b) { cout<<"模板函数"<<endl; } template<> void fun<int , char >(int a, char b) { cout<<"全特化"<<endl; }类模板
template<typename T1, typename T2> class Test { public: Test(T1 i,T2 j):a(i),b(j){cout<<"类模板"<<endl;} private: T1 a; T2 b; }; template<> class Test<int , char> { public: Test(int i, char j):a(i),b(j){cout<<"全特化"<<endl;} private: int a; char b; }; template <typename T2> class Test<char, T2> { public: Test(char i, T2 j):a(i),b(j){cout<<"偏特化"<<endl;} private: char a; T2 b; }
STL
六大组件:容器、算法、迭代器、仿函数、适配器、空间配置器
关系:
- 容器通过空间配置器获得存储空间
- 算法通过迭代器访问空间中的内容
- 仿函数增加算法的通用性
- 适配器可以修饰仿函数
STL将数据和操作分离,由迭代器充当交互的中间人。
关于容器的接口函数,建议在需要用到的时候,直接查看cplusplus的文档,里面介绍的很详细,不推荐死记硬背。在刚开始接触容易的时候,只要知道有哪些容器,大致是什么用途就好了。
以下是简要介绍
pair
作用:将两个数据成员进行组合,通常和map一起进行使用,作为map的元素。
在map中插入(insert)时,返回值为<迭代器, bool>,迭代器指向插入的元素,bool指示插入是否成功。
map.insert({a, b}); map.insert(pair<string, string>(a, b));
vector
作用:动态数组
底层实现:堆中的连续内存。如果当前空间已经用完,则在新增数据时,分配一块更大的内存(通常是翻倍),将原来的数据复制过来。
扩容机制:(1)固定扩容 (2)加倍扩容
迭代器:由于vector是线性空间,所以普通指针具备作为vector迭代器的所有条件。
数据结构:线性空间
源码的大致思路(注意,实际的源码要复杂得多)
templeta<class T,class Alloc=alloc> class vector { public: typedef T value_type; typedef value_type *pointer; typedef value_type &reference; typedef value_type *iterator; typedef size_t size_type; typedef ptrdiff_t difference_type //类型定义 protected: typedef simple_alloc <value_type, Alloc> data_alloctor //空间配置器 iterator start; iterator finish; iterator end_of_storage; //3个指针,作为迭代器,指向start,end, end_of_storage //自动扩充内存 void insert_aux(iterator position, const T &x); //取消分配内存,用于析构函数 void deallocate() { if (start) data_allocator::deallocate(start, end_of_storage); } //初始化填充,用于构造函数 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; }; iterator end() { return finish; }; size_type size() const { return size_type(end() - begin()); }; 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); }; //constructor vector() : start(0), end(0), end_of_storage(0) {}; vector(size_type n, const T &value)(fill_initialize(n, value);); vector(long n, const T &value)(fill_initialize(n, value);); vector(int n, const T &value)(fill_initialize(n, value);); explicit vector(size_type n) { fill_initialize(n, T()); }; //析构 ~vector() { destory(start, finish);//全局函数,析构对象 deallocate();//成员函数,释放空间 } //member function reference front() { return *begin(); }; reference back() { return *(end() - 1); }; void push_back(const T &x) { if (finsih != end_of_storage) { construct(finish, x); ++finish; } else insert_aux(end(), x); //扩充后添加 } void pop_back() { --finish; destory(finish); } iterator erase(iterator position) { if (position + 1 != end()) copy(position + 1, finish, position); --finish; destory(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); uninitialized_fill_n(result, n, x);//全局函数 } }构造和内存管理:在末尾插入元素时,有空间则插入,无空间则扩容后再插入。
template<class T, class Alloc> void vector<T, Alloc>::insert_aux(iterator position, const T &x) { if (finish != end_of_storage) { //有空余空间则直接构造,注意allocate, deallocate, construct, destory, max_size为库函数,可直接调用。 //在alloc_traits.h头文件中 consruct(finish, *(finish - 1)); ++finish; T x_copy = x; copy_backward(position, finish - 2, finish - 1); *position = x_copy; } else { //否则,翻倍申请新空间,并将原空间的元素拷贝过来 const size_type old_size = size(); const size_type len = old_size != 0 ? 2 * old_size() : 1; iterator new_start = data_allocator::allocate(len); iterator new_finish = new_start; try { //尝试拷贝原有元素,失败则抛出异常 new_finish = uninitialized_copy(start, position, new_start); construct(new_finish, x); ++new_finish; new_finish = uninitialized_copy(position, finish, new_finish); }catch () { destroy(new_start, new_finish); data_allocator::deallocate(new_start, len); throw; } //删除原vector destory(begin(), end()); deallocate(); //更新指针 start = new_start; finish = new_finish; end_of_storage = new_start + len; } }
list
作用:双向链表
迭代器:迭代器是泛化的指针,重载了->, --, ++, * ()等运算符,迭代器也是算法与容器之间的桥梁,算法需要了解容器的方方面面,于是就有了5种关联类型。我们通过萃取器(iterator traits类)来提供关联类型值。具体细节参阅c++primer相关章节。
template<class T, class Alloc = alloc> class list { protected: typedef listnode <T> listnode; public: typedef listnode link_type; typedef listiterator<T, T &, T> iterator; protected: link_type node; };迭代器的5种关联类型
- value_type
- difference_type
- pointer
- reference
- iterator_vategory
// 迭代器内定义的关联类型 template<class I> struct iterator_traits { typedef typename I::iteratorcategory iteratorcategory; typedef typename I::valuetype valuetype; typedef typename I::differencetype differencetype; typedef typename I::pointer pointer; typedef typename I::reference reference; }; •// 指针类型的迭代器 template<class T> struct iteratortraits<T> { typedef randomaccessiteratortag iteratorcategory; typedef T value_type; typedef ptrdifft differencetype; typedef T *pointer; typedef T &reference; }; // 指针常量类型的迭代器 template<class T> struct iteratortraits<const T> { typedef randomaccessiteratortag iteratorcategory; typedef T valuetype; typedef ptrdifft differencetype; typedef const T *pointer; typedef const T &reference; };
deque
作用:双端数组
底层实现:分段连续的空间,随时可以增加一段新空间并链接。效率显著低于vector,为了提高效率,在对deque进行排序时,可以先把deque复制到vector中排序,再复制回去。
中控器:一段一段的定量连续空间组成
数据结构:一个map,first和finish两个迭代器,map的大小
template<class T, class Alloc=alloc, size_t Bufsize = 0> class deque { public: typedef T value_type; typedef value_type pointer*; typedef size_t size_type; // ... public: typedef _deque_iterator<T, T &, T *, BufSiz> iterator; protected: typedef pointer *map_pointer; protected: iterator start; iterator finish; map_pointer map;//指向map size_type map_size;//map可以容纳的指针数 }迭代器
template<class T, class Ref, class Ptr, size_t BufSiz> struct _deque_iterator { typedef _deque_iterator<T, T &, T *, BufSiz> iterator; typedef _deque_iterator<T, cosnt T &, const T *, BufSiz> const_iterator; static size_t buffer_size() { return _deque_buf_size(BufSiz, sizeof(T)); }; typedef randem_access_iterator_tag iterator_category; typedef T value_type; typedef Ptr pointer; typedef Ref reference; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef T **map_pointer; typedef _deque_iterator self; T *cur; T *first; T *last; map_pointer node; // ... //指定缓存区的大小 inline size_t _deque_buf_size(size_t n, size_t sz) { return n != 0 ? n : (sz < 512 ? size_t(512 / sz) : size_t(1)); } }
stack && queue
概述:以deque为底层架构的适配器;
heap && priority_queue
heap:堆,建立在以vector表现的完全二叉树上,作为priority_queue的助手,
priority_queue:优先级队列,也是适配器
map && set
都是C++的关联容器,底层都是采用红黑树实现。
set用来判断一个元素是否在结合中
map用来做映射
查找和插入元素都是O(logn)
map && unordered_map
map由红黑树实现,内部元素有序
unordered_map由哈希表实现,内部元素无序
查找插入删除都是O(1)
在哈希函数不合适时,时间复杂度可能为O(n)
insert后迭代器不会失效,因为只是插入节点,没有重新分配内存
遇到不会的知识点,欢迎在回复中提问,我会在回复中回答,或者另写文章具体讲解。
祝大家都能在秋招中斩获心仪的offer。
感谢阅读。