C++STL
基础
- 泛型编程:指以模板为主要工具进行程序的编写
- c++标准库(Standard Library)包含c++标准模板库(Standard Template Library)
- 标准库以头文件形式存在
- C++标准库的头文件不带
.h
- 新式的C头文件不带
.h
,旧式的C头文件仍要使用.h
- C++标准库的头文件不带
- 所有的新式的头文件都封装在命名空间
std
中
STL Application
-
STL的六大部件
- 容器Containers:解决了数据存放,是一个class template
- 分配器Allocators:对容器进行内存管理,是一个class template
- 算法Algorithms:对容器中的数据进行处理,是一个function template
- 迭代器Iterators:对容器中的数据进行遍历访问,是一个class template
- 适配器Adapters:数据格式的转换以适用函数参数的调用,是一个class template
- 仿函数Functors:是一个class template
-
STL思想:将数据和算法分离,算法借助迭代器进行对于容器的数据进行操作
-
前闭后开 :容器中begin指向第一个元素,而end指向最后一个元素的下一个元素首地址,所以对于解引用end得到的元素通常是无意义的
-
C++11的容器遍历语法
for( auto 遍历指针 : 容器 ){// 指针类型是容器元素的类型,auto可以自动推导 // 每次循环指针依次获取容器内的一个元素 } // auto的使用的前提是你必须知道该声明变量的类型
-
容器的结构与分类
-
顺序容器
- Array:在运行前即确定的一片连续的空间,就是通常意义的数组
- Vector: 首地址固定,当空间不够时,找一段是之前两倍大小的空间做为拓展
- Deque:双端可扩充,是离散分段连续的
- List:元素间不连续,使用双向指针连接
- Forward-List:单向链表,额外空间比双向链表少
-
关系容器
-
Set/Multiset:每个节点不区分键值,Multi表示主键可以重复
-
Map/Multimap:每个节点包含一个key和一到多个value,常用红黑树
注:红黑树是自平衡二叉查找树
-
-
关联式容器
- Unordered Map/Multimap:先常用数组链进行存储,数组存放key,碰撞的值存储在该key形成的链表中
- Unordered Set/Multiset:key和value是同一个,使用数组链存储,就是hash表
- 注意:该容器具有数据库的键值特性,通常具有自己特化函数
-
-
每一段功能代码使用namespace进行包含,namespace上面声明其需要的库文件
-
可拓展的容器存在空间利用率降低的问题
-
STL中的stack和queue是借用容器的一种适配器结构
-
通常来说,容器自身的属性函数调用运行时间比标准库中的全局函数快
-
STL模板算法示例
// 算法accumulate,对某个区间的值进行指定运算 template < class InputIterator, class T, class BinaryOperation> T accumulate( InputIterator first, InputIterator last, T init, BinaryOperation binary_op) { for(;first!= last; ++first) // 将元素值累加到初值init上 init = binary_op(init, *first);// 每次调用func函数 return init; } // 功能函数 int myfunc(int x, int y){return x+2*y} //函数雕调用 void test_accumulate(){ int init = 100; int nums = {1, 2, 3}; accumulate(nums, nums+3, init, myfunc);// 220 } // for_each算法:对区间的每个元素做指定的事情 template<class InputIterator, class Function> Function for_each(InputIterator first, InputIterator last, Function f){ for(;first != last; ++first) f(*first); return f; } // 函数式功能调用 void myfunc(int i){cout << '' << i;} // 对象式调用,进行了操作符的重载 struct myclass{ void operator(int i){cout << '' << i;} }myobj; void test_for_each(){ vector<int> myvec; myvec.push_back(10); myvec.push_back(20); myvec.push_back(30); // 下面两个模板调用均输出:10,20,30 for_each(myvec.begin(), myvec.end(), myfunc); for_each(myvec.begin(), myvec.end(), myobj); } // replace算法:范围内所有等于old_value者都以new_value取代 template<class ForwardIterator, class T> void replace(ForwardIterator first, ForwardIterator last, const T& old_value, const T& new_value) { for(;first != last; ++first) if(*first == old_value) *first = new_value; } // count_if算法:可以对指定区间内的每个元素进行条件函数判断,复合的才计数 // 该算法式泛化的,在无序容器中有其特化的cout成员函数 template<class InputIterator, class Predicate> typename iterator_traits<InputIterator>::difference_type count_if(InputIterator first, InputIterator last, Predicate pred){ typename iterator_traits<InputIterator>::difference_type n = 0; for(; first != last; ++first)// 区间遍历 if(pred(*first))// 将元素带入条件判断式为真 ++n; return n; } // find算法:对区间内的值进行顺序查找 template<class InputIterator, class T> InputIterator find (InputIterator first, InputIteratro last, 4 const T& value) { while(first != last && *first != value) ++first; return first; } // binary_search算法:在已排序的容器中进行二分查找 // lower_bound:在区间内适合值存放的低地址安插val template<class ForwardIterator, class T> ForwardIterator lower_bound(ForwardIterator first, forwardIterator last, const T& val) { ForwardIterator it; iterator_traits<ForwardIterator>::difference_type count,step; count = distance(first, last); while(count > 0){ it = first; step = count / 2; advance(it, strp);// 前进步数 if(*it < val){ first = ++it; count -= step++; }else count = step; } return first; } // binary_search实际调用的是lower_bound函数 template<class ForwardIterator, class T> bool binary_search(ForwardIterator first, ForwardIterator last, const T& val) { first = std::lower_bound(first, last, val); return (first != last; && !(val < *first)); }
分配器
-
直接使用分配器,在free空间的时候,需要与申请空间大小相同,所以不直接使用
-
小的内存空间使用new和delete,大的内存空间使用容器进行
-
标准库中的源代码是一个宝库
-
VC6下,分配器allocator类方法内有allocate和deallocate,分别包含
operator new ()
和operator delete ()
,而operator new ()
会调用malloc()
进行内存的分配。operator delete ()
调用free()
-
malloc分配的内存通常更大一些,还有在头尾附加的cookie和尾部附加的padding,所以不要经常使用小块内存多次申请
-
使用分配器申请512个int类型
// 通常由容器进行调用分配器的函数,如果个人调用,那么容易忘记内存空间的大小 int *p = allocate<int>().allocate(512,(int*)0);// 使用临时对象进行内存的分配 allocator<int>().deallocate(p, 512);// 使用临时对象进行内存的销毁
-
G2.9中aloc容器的元素大小会被调整到8的倍数,使用数组链进行,数组内一次挂载8的倍数的内存开销,避免了cookie的开销
-
标准库中更多的是复合关系,而不是继承。继承和复合都可以实现A使用B的功能
-
迭代器iterator是算法和容器的桥梁
-
迭代器要定义的五种类型,回应算法的需求
- iterator_catagory:返回哪一种迭代步长
- difference_type_Distance:表示两个迭代器的距离
- value_type_ValueType:迭代器指向的值的类型
- reference_type
- pointer_type
-
指针是一种native pointer,一种退化的interator
-
解决计算机问题的尚方宝剑:加一个中间层。
例如native pointer无法进行type提取的问题,使用一个中间层萃取机iterator Traits通过偏特化进行实现
容器
-
list使用的分配器默认是alloc,使用递增数组链进行内存管理
-
32位电脑上一个指针是四个字节
-
list容器中的链表结构体是一个环状的双向链表,有额外的开销
// 库文件的设计是一种更高的抽象 template<class T> // 这种设计不够好,转型需要额外操作 struct __list_node{ typedef void *void_pointer;// 定义泛型,实际使用需要进行转型 void_pointer prev; void_pointer next; T data; }; // 新设计 struct _List_node_base{ _List_node_base* _M_next; _List_node_base* _M_prev; }; template<typename _Tp> struct _List_node :public _List_node_base{_Tp _M_data;};
-
容器中iterator的作用是指向一个元素,并可以通过++指向下一个元素
-
所有的容器(处理voctor和array),iterator都是一个将其模拟为特定smart pointer的类,可以自动找到下一个容器元素的地址
template<class T, class Ref, class Ptr> struct __list_iterator{ // 迭代器至少定义的五种类型 typedef __list_iterator<T, Ref, Ptr> self; typedef bidirectional_iterator_tag iterator_category; 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);// 取next指针设置为自身 return *this; } // 后++有参数,c++规定后++不能连续两次,所以不使用reference(引用) self operator++(int){ self tmp = *this;// 记录原值,不会调用operator*而是调用拷贝构造 ++*this;// 执行操作 return tmp;// 返回原值 } ··· }
-
容器的设计都要前闭后开,所以list的设计中将最后一个元素设计为不属于该容器的元素
-
vector容器,每次空间不够扩充时,就到内存中寻找一个与前一段空间相比两倍大小的空间作为扩充空间,将原数据拷贝到前半段,即每次成长需要调用拷贝构造函数和析构函数
-
array容器必须指定大小,不可扩充
-
所有的容器几乎都含有两个iterator分别表示首和尾,iterator通常是属性指针组合的一个数据结构
-
typeid可以打印容器的类型的名称,必须包含
#include <typeinfo>
-
deque容器
- 使用interator进行连续空间的模拟,实际是离散的相同大小的空间段
- 迭代器进行加减操作需要计算跳跃缓冲区数量
- 在进行插入操作时,插入位置的前和后会进行比较大小,选择较小的进行复制移动
-
基本容器的关系
- deque:双向进出
- stack:先进后出,底层为deque封住了一端
- queue:先进先出,底层为deque两端均封住了一些功能
关联式容器
-
关联式容器类似于一个小型数据库,查找速度快,即可以使用key查找value
-
红黑树特点
- 一个平衡的二分搜索树,避免某一条通路太长导致搜索效率慢
- 元素自动排序:通过迭代器++操作进行顺序遍历可以得到一个排序 ,即中序搜索序列
- 不能使用红黑树的迭代器进行赋值操作,因为可能破坏红黑树的排序状态
- header节点记录最小节点和最大节点
-
map允许元素的data改变,但是元素的key不可改变
-
key和data合起来是value
-
rb_tree提供两种insertion操作
- insert_unique():key不可重复
- insert_equal():key可以重复
-
set和mutiset的特点
-
都以rb_tree为底层的结构,进行++ite可以获得排序,value和key是合二为一的
-
set指元素的key不可重复,mutiset指元素的key可以重复
-
不能通过iterator进行元素值的更改(通过const_iterator )
-
可以看作一个容器的适配器,所有实际操作都是调用底层红黑树进行实现
-
-
map和multimap类似,唯一的不同是value表示的是key和data(禁止改key而允许改data)
// 代码原理 template< class Key, class T, class Compare = less<Key>, class Alloc = alloc > class map{ typedef Key key_type; typedef T data_type; typedef pair<const Key, T> value_type;// key为const不可改 //底层实现 typedef rb_tree<key_type, value_type, select1st<value_type>, key_compare, Alloc> rep_Type; ··· }
-
map独有的
[]
操作符:如果存在key则返回所表示的data,如果不存在则创建 -
针对同一个操作的不同使用对象提供泛化和特化,泛化适用于该类对象,特化适用于某个对象,并且特化通常效率高。泛化和特化智能的找到效率最高的处理方法
hashtable容器
-
使用一个统一的可量化的标准对一堆对象进行存放并索引
-
hashtable的特点
- 映射函数应该尽量减少碰撞的次数
- 使用数组链数据结构进行索引,数组储存hash值,链表表示同一hash值的碰撞(通常如果单链长度大于数组元素个数,就进行数组的翻倍拓展并重新计算)
-
标准库中没有提供C++的string的hashFcn
-
hash容器中数组元素个数一定大于实际使用的元素个数
-
代码构造
// HashFcn表示对象到特定编号的映射函数,标准是碰撞尽量少 // ExteactKey表示从value中提取key的方法 // EqualKey表示如何进行key值的比较 template< class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc = alloc>
-
容器大小
- vector对象通常是12个字节
- 类中的class对象通常作为方法只占用1个字节
- hashtable为19个字节,而调整为4的倍数占用20个字节
-
C++11中所有的hash容器改名为unordered容器
迭代器
-
迭代器是容器和算法的中间层,用于屏蔽容器细节向算法提供服务
// 算法的模板 template<typename Iterator> Algorithm(Iterator itr1, Iterator itr2) { ··· }
-
迭代器属性iterator_category的继承
template< typename _Category, typename _Tp, typename _Distance = ptrdiff_t, typename _Potinter = _Tp*, typename _Reference = _Tp& > struct iterator { typedef _Category iterator_category; typedef _Tp value_type; typedef _Distance difference_type; typedef _Pointer pointer; typedef _Reference reference; }; template< typename _Tp, typename _CharT = char, typename _Traits = char_traits<_CharT> >class ostream_iterator :public itereator<output_iterator_tag, void, void, void, void>
-
算法需要知道迭代器的分类iterator_category
- 判断空间是否连续,从而执行不同的distance
-
逆向迭代器
reverse iterator
// rbegin和区间的end地址相同 reverse_iterator rbegin(){ return reverse_iterator(end()); } // rend和区间的begin地址相同 reverse_iterator rend(){ return reverse_iterator(begin()); }
仿函数functors
-
STL规定每个Adaptable Function都要挑选合适的模板继承,因为适配器可以进行修改函数的修改,而修改过程需要对于typedef定义的参数进行提问
// 一个操作数的模板 template<class Arg1, class Arg2, class Result> struct binary_function{ typedef Arg1 first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type; }; // 两个操作数的模板 template<class Arg1, class Arg2, class Result> struct binary_function{ typedef Arg1 first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type; };
-
几个仿函数
// struct;定义的类默认成员访问级别为private // 类后的冒号表示继承关系 // 算术类Arithmetic template<class T> struct plus :public binary_function<T, T, T>{ T operator()(const T& x, const T& y)const {return x+y;} }; template<class T> struct minus:public binary_function<T, T, T>{ T operator()(const T& x, const T& y)const {return x-y;} }; // 逻辑运算类Logical template<class T> struct logical_and:public binary_function<T, T, bool>{ bool operator()(const T& x, const T& y)const {return x&&y;} }; // 相对关系类Relatioinal template<class T> struct equal_to:public binary_function<T, T, bool>{ bool operator()(const T& x, const T& y)const {return x == y;} }; template<class T> struct less:public binary_function<T, T, bool>{ bool operator()(const T &x, const T &y)const {return x < y;} };
-
比大小的应用
// 默认方式 sort(myvec.begin(), myvec.end()); // 使用函数形式 bool myfunc(int i, int j){return (i<j);} sort(myvec.begin(), myvec.end(), myfunc); // 使用类形式 struct myclass{ bool operator()(int i, int j){return (i < j);} }myobj; sort(myvec.begin(), myvec.end(), myobj); // 使用仿函数 template<class T> struct less:public binary_function<T, T, bool>{ bool operator()(const T &x, const T &y)const {return x < y;} }; sort(myvec.begin(), myvec.end(), less<int>());
-
Adapters的作用是将存在的功能进行改造一下
-
适配器的类型(表示改造对应内含实体的功能)
- Container Adapters
- Iterator Adapters
- Functor Adapters
-
容器适配器:改造容器的功能
template <class T, class Sequence=deque<T>> class stack{ ··· public: typedef typename Sequence::value_type value_type; typedef typename Sequence::size_type size_type; typedef typename Sequence::referrnce reference; typedef typename Sequence::const_reference const_reference; // typename用于帮助编译器进行自动类型推导 protected: Sequence c; // 只开放了六个容器的功能,并进行名称的改造 public: bool empty()const{return c.empty();} size_type size() const {return c.size();} reference top(){return c.back();} const_reference top() const{return c.back();} void push(const value_type &x){c.push_back(x);} void pop(){c.pop_back();} };
-
c++的编译器可以进行做实参的推导
-
适配器可以对于一个源代码进行不断的装甲,使得其可以完成不同的功能
-
C++11增加的适配器bind,可以绑定的
- functions
- function objects
- member functions
- data members
-
_数字
表示一个占位符,需要添加using namespace std::placeholders
-
bind的用法
using namespace std::placeholders // 函数的绑定 double my_divide(double x, double y){return x / y;} // 直接绑定 auto fn_five = bind(my_divide, 10, 2); cout << fn_five() << '\n'; // 含一个占位符的绑定 auto fn_five = bind(my_divide, _1, 2); cout << fn_five(10)<< '\n'; // 含两个占位符的绑定,_1表示调用绑定函数时候的第一个参数 auto fn_five = bind(my_divide, _1, _2); cout << fn_five(10, 2) << '\n'; // 含占位符并指定返回类型的绑定 auto fn_rounding = bind<int>(my_divide, _1, _2); cout << fn_rounding(10, 3) << '\n'; // 成员的绑定 Mypair ten_two {10, 2}; // 直接绑定 auto bound_memfn = bind(&MyPair::a, ten_two); cout << bound_memdata(); // 含成员的绑定 auto bound_memfn = bind(&MyPair::multiply, _1); cout << bound_memfn(ten_two) << '\n';
-
模板的使用后,编程出现一个错误可能会很多行的全局错误
面向对象编程OOP和泛型编程GP
-
OOP是数据和操作方法放在同一个类中,GP是将数据和操作方法分离
-
GP可以使得算法和容器团队分开各自工作,只要规范接口即可
-
不可重载的四个运算符
. .* :: ?:
-
C++中tuple的使用
// 使用的多参数可变技术:可以将n个参数分解为最后一个和剩余部分(递归到最后一个) template<typename... Values>class tuple; template<>class tuple<>{};// 递归终止的偏特化 template<typename Head, typename... Tail> class tuple<Head, Tail...>// 类的递归 :private tuple<Tail...> { typedef tuple<Tail...> inherited; public: tuple(){} tuple(Head v, Tail... vtail) :m_head(v), inherited(vtail...){} typename Head::type head(){return m_head;} inherited& tail(){return *this;} protected: Head m_head; }; tuple<int, float, string> t1(1, 1.0, "aa")// 定义方式 auto t2 = make_tuple(22, 44, "ss");// 定义方式2 cout << "t1:"<<get<0>(t1) << '' << get<1>(t1) << get<2>(t1);// 输出 get<1>(t1) = get<1>(t2);// 取值并赋值 // 多参数可变的应用 t1.head();// 值为1 t1.tail().head();// 值为1.0 // 解耦合 int i; float f; string s; tie(i, f, s) = t1;// 将t1元素解耦合赋值给不同变量
-
type traits的泛化和特化
// 可以了解自定义或库中类所能支持的功能 // 泛化 template <class type> struct __type_traits{ // true 和 false 表示该标志是否重要 typedef __true_type this_dummy_member_must_be_first; typedef __false_type has_trivial_default_constructor; typedef __false_type has_trivial_copy_constructor; typedef __false_type has_trivial_assignment_operator; typedef __false_type has_trivial_destructor; typedef __false_type is_POD_type; }; // 偏特化 struct __type_traits<char*> { typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type; }; struct __type_traits<signed char*> { typedef __true_type has_trivial_default_constructor; typedef __true_type has_trivial_copy_constructor; typedef __true_type has_trivial_assignment_operator; typedef __true_type has_trivial_destructor; typedef __true_type is_POD_type; }; // 算法对于typedef标志定义的询问 __type_traits<Foo>::has_trivial_destructor
-
一个类只要在作为基类时,才可能是virture
-
cout是一个对象,内含对
<<
操作符的重载 -
moveable对于扩充型容器可以极大的提高速度,因为它在容器的扩充中只拷贝指针
- movecopy之后原来的不可使用
-
深拷贝:不但把指针进行拷贝,指针指向的内容也进行拷贝。浅拷贝很危险
栈容器的使用https://blog.csdn.net/gongkeguo/article/details/122049270