STL杂记
STL介绍:
我所理解的stl:
容器: 是一种数据结构,如list,vector,和deques ,以模板类的方法提供。为了访问容器中的数据,可以使用由容器类输出的迭代器;
算法: 是用来操作容器中的数据的模板函数。例如,STL用sort()来对一个vector中的数据进行排序,用find()来搜索一个list中的对象,函数本身与他们操作的数据的结构和类型无关,因此他们可以在从简单数组到高度复杂容器的任何数据结构上使用;
迭代器: 提供了访问容器中对象的方法。例如,可以使用一对迭代器指定list或vector中的一定范围的对象。迭代器就如同一个指针。事实上,C++的指针也是一种迭代器。但是,迭代器也可以是那些定义了operator*()以及其他类似于指针的操作符地方法的类对象;
仿函数: 就是一个类里面重载() 所创建出来的对象,叫做函数对象,或者仿函数
迭代适配器(Adaptor):
空间配制器(allocator): 分配内存的, 偏底层
算法的使用介绍
在stl的算法中分为两大块:一个是全局(泛化)的,一个是容器本身所有的
在使用一个算法时首先看你使用的容器中是否有这个算法,如果有的话,就用容器本身有的
(注意不能使用泛化的,因为泛化的可能不能用这个容器,或者没有容器自带的效率高)
如果没有的话,就使用泛化的
注意这几个例子:图片左边是算法的实现,右边是哪个容器本身有这个算法
count和count_if使用
源码:
/**对谓词为真的序列的元素进行计数。 * @brief Count the elements of a sequence for which a predicate is true. * @ingroup non_mutating_algorithms * @param __first An input iterator. * @param __last An input iterator. * @param __pred A predicate. * @return The number of iterators @c i in the range @p [__first,__last) * for which @p __pred(*i) is true. */ template<typename _InputIterator, typename _Predicate> inline typename iterator_traits<_InputIterator>::difference_type count_if(_InputIterator __first, _InputIterator __last, _Predicate __pred) { return std::__count_if(__first, __last, __gnu_cxx::__ops::__pred_iter(__pred)); }
使用:
// // Created by lk on 18-6-4. // #include <algorithm> #include <iostream> #include <vector> #include <set> using namespace std; bool myfun(int i){return i > 4;} struct myClasss{ bool operator()(int i){return i > 4;} }myObjj; int main() { // 泛化的 vector<int> vec = {1,2,3,4,5,5,6,3,4}; auto num = count(vec.begin(), vec.end(), 3); cout << "vec中3的个数为"<< num << endl; auto t = count_if(vec.begin(), vec.end(), myfun); auto t2 = count_if(vec.begin(), vec.end(), myObjj); cout << t << " " << t2 << endl; // 结果为3 3 // 容器本身自带的函数 set<int> s1 = {1,2,3,4,5}; auto s_num = s1.count(3); cout << s_num << endl; // 1 return 0; }
find方法:
template<typename _IIter, typename _Tp> _IIter find(_IIter, _IIter, const _Tp&); template<typename _FIter1, typename _FIter2> _FIter1 find_first_of(_FIter1, _FIter1, _FIter2, _FIter2); template<typename _FIter1, typename _FIter2, typename _BinaryPredicate> _FIter1 find_first_of(_FIter1, _FIter1, _FIter2, _FIter2, _BinaryPredicate); template<typename _IIter, typename _Predicate> _IIter find_if(_IIter, _IIter, _Predicate);
// // Created by lk on 18-6-4. // #include <algorithm> #include <iostream> #include <vector> #include <set> using namespace std; namespace luck_count{ bool myfun(int i){return i > 4;} struct myClasss{ bool operator()(int i){return i > 4;} }myObjj; int test_count() { // 泛化的 vector<int> vec = {1,2,3,4,5,5,6,3,4}; auto num = count(vec.begin(), vec.end(), 3); cout << "vec中3的个数为"<< num << endl; auto t = count_if(vec.begin(), vec.end(), myfun); auto t2 = count_if(vec.begin(), vec.end(), myObjj); cout << t << " " << t2 << endl; // 结果为3 3 // 容器本身自带的函数 set<int> s1 = {1,2,3,4,5}; auto s_num = s1.count(3); cout << s_num << endl; // 1 return 0; } } namespace luck_find{ bool myFun(int i) {return i > 2;} struct MyClass{ bool operator()(int i) {return i > 2;} }myObj; void test() { vector<int> vec = {1,2,3,4,5,5,6,3,2}; auto it = find(vec.begin(), vec.begin()+5, 6); cout << "it是6的迭代器地址" << endl; auto it2 = find_if(vec.begin(), vec.end(), myFun); cout << "use custom functional" << endl; cout << "it2是大于2的迭代器地址" << endl; auto it3 = find_if(vec.begin(), vec.end(), myObj); cout << "use custom object" << endl; cout << "it3是大于2的迭代器地址" << endl; } } int main() { luck_count::test_count(); luck_find:: test(); return 0; }
sort用法
包含仿函数
/**使用比较的谓词来排序序列的元素。 * @brief Sort the elements of a sequence using a predicate for comparison. * @ingroup sorting_algorithms * @param __first An iterator. * @param __last Another iterator. * @param __comp A comparison functor. * @return Nothing. * * Sorts the elements in the range @p [__first,__last) in ascending order, * such that @p __comp(*(i+1),*i) is false for every iterator @e i in the * range @p [__first,__last-1). * * The relative ordering of equivalent elements is not preserved, use * @p stable_sort() if this is needed. */ template<typename _RandomAccessIterator, typename _Compare> inline void sort(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp) { // concept requirements __glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept< _RandomAccessIterator>) __glibcxx_function_requires(_BinaryPredicateConcept<_Compare, typename iterator_traits<_RandomAccessIterator>::value_type, typename iterator_traits<_RandomAccessIterator>::value_type>) __glibcxx_requires_valid_range(__first, __last); __glibcxx_requires_irreflexive_pred(__first, __last, __comp); std::__sort(__first, __last, __gnu_cxx::__ops::__iter_comp_iter(__comp)); }
#include <algorithm> #include <iostream> #include <vector> #include <set> #include <functional> namespace luck_sort{ void print(vector<int> &vec) { for (auto item : vec) cout << item << " "; cout << endl; } bool myfun(int i, int j){return i > j;} struct myclass{ bool operator ()(int i, int j){return i < j;} }myObj; // 仿函数对象省略了 void test() { vector<int> vec = {1,2,5,6,2,3,0,-3}; sort(vec.begin(), vec.end()); cout << "use default 谓词"<<endl; print(vec); cout << "use default functional"<<endl; sort(vec.begin(), vec.end(), less<int>()); // greater<int>() 从大到小排序 print(vec); sort(vec.begin(),vec.end(), myfun); cout << "use custom fun" << endl; print(vec); sort(vec.begin(),vec.end(), myObj); cout << "use custom object" << endl; print(vec); cout << endl; } } int main() { // luck_count::test_count(); // cout << endl; // luck_find:: test(); // cout << endl; luck_sort:: test(); return 0; }
仿函数
仿函数:就是一个类里面重载() 所创建出来的对象,叫做函数对象,或者仿函数
有些功能的的代码,会在不同的成员函数中用到,想复用这些代码。
1)公共的函数,可以,这是一个解决方法,不过函数用到的一些变量,就可能成为公共的全局变量,再说为了复用这么一片代码,就要单立出一个函数,也不是很好维护。
2)仿函数,写一个简单类,除了那些维护一个类的成员函数外,就只是实现一个operator(),在类实例化时,就将要用的,非参数的元素传入类中
c和c++中的仿函数
1)c语言使用函数指针和回掉函数来实现仿函数,例如排序函数中使用仿函数:
#include <stdio.h> #include <stdlib.h> //int sort_function( const void *a, const void *b); int sort_function( const void *a, const void *b) { return *(int*)a-*(int*)b; } int main() { int list[5] = { 54, 21, 11, 67, 22 }; qsort((void *)list, 5, sizeof(list[0]), sort_function);//起始地址,个数,元素大小,回调函数 for (auto x = 0; x < 5; x++) printf("%d\n", list[x]); return 0; }
2)在C++里,我们通过在一个类中重载括号运算符的方法使用一个函数对象而不是一个普通函数。
#include <iostream> #include <algorithm> using namespace std; template<typename T> class display { public: void operator()(const T &x) { cout<<x<<" "; } }; int main() { int ia[]={1,2,3,4,5}; for_each(ia,ia+5,display<int>()); //循环遍历数组 return 0; }
仿函数在STL中的定义
要使用STL内建的仿函数,必须包含<functional>头文件。而头文件中包含的仿函数分类包括
1)算术类仿函数
加:plus<T>
减:minus<T>
乘:multiplies<T>
除:divides<T>
模取:modulus<T>
否定:negate<T>
#include <iostream> #include <numeric> #include <vector> #include <functional> using namespace std; int main() { int ia[]={1,2,3,4,5}; vector<int> iv(ia,ia+5); cout<<accumulate(iv.begin(),iv.end(),1,multiplies<int>())<<endl; //加法 cout<<multiplies<int>()(3,5)<<endl; //乘法 modulus<int> modulusObj; //取模运算 cout<<modulusObj(3,5)<<endl; // 3 return 0; }
2)关系运算类仿函数
等于:equal_to<T>
不等于:not_equal_to<T>
大于:greater<T>
大于等于:greater_equal<T>
小于:less<T>
小于等于:less_equal<T>
从大到小排序:
#include <iostream> #include <algorithm> #include <vector> using namespace std; template <class T> class display { public: void operator()(const T &x) { cout<<x<<" "; } }; int main() { int ia[]={1,5,4,3,2}; vector<int> iv(ia,ia+5); sort(iv.begin(),iv.end(),greater<int>()); for_each(iv.begin(),iv.end(),display<int>()); return 0; }
3)逻辑运算仿函数
逻辑与:logical_and<T>
逻辑或:logical_or<T>
逻辑否:logical_no<T>
bind和适配器
如果想要统计和比20大的数,用自带的仿函数不行,又不像自己写,或者自己写,要写很多,所以用适配器,绑定参数,这里是绑定less中的第二个参数, 原本less中是x>y 现在就变成x>20
这里的调用过程: 在vec的序列中,统计不满足后面的条件的数的个数
Less<int>()是一个临时的仿函数对象(class A ==> A a()) ,不是调用函数
但是现在bind2nd被改成bind bind的功能更加强大,绑定很多个参数
这里主要讲bind的使用
#include <algorithm> #include <iostream> #include <vector> #include <set> #include <functional> namespace luck_bind{ // bind(函数对象,仿函数对象, 对象要的参数_1,_2,...) template<typename T> void print(vector<T> &vec) { for (auto item : vec) { cout << item << " "; } cout << endl; } // a function: (also works with function object: std::divides<double> my_divide;) double my_divide (double x, double y) {return x/y;} struct MyPair { double a,b; double multiply() {return a*b;} }; void test() { // 这句话必须写, 还有functional头文件 using namespace std::placeholders; // adds visibility of _1, _2, _3,... // binding functions: auto fn_five = std::bind (my_divide,10,2); // returns 10/2 std::cout << fn_five() << '\n'; // 5 auto fn_half = std::bind (my_divide,_1,2); // returns x/2 std::cout << fn_half(10) << '\n'; // 5 auto fn_invert = std::bind (my_divide,_2,_1); // returns y/x std::cout << fn_invert(10,2) << '\n'; // 0.2 auto fn_rounding = std::bind<int> (my_divide,_1,_2); // returns int(x/y) std::cout << fn_rounding(10,3) << '\n'; // 3 MyPair ten_two {10,2}; // binding members: auto bound_member_fn = std::bind (&MyPair::multiply,_1); // returns x.multiply() std::cout << bound_member_fn(ten_two) << '\n'; // 20 auto bound_member_data = std::bind (&MyPair::a,ten_two); // returns ten_two.a std::cout << bound_member_data() << '\n'; // 10 vector<int> vec = {1,5,2,3,4,-1,6}; cout << "bind and bind2nd different"<<endl; cout << count_if(vec.begin(), vec.end(), not1(bind2nd(less<int>(), 4))) << endl; cout << "use bind of count_if"<<endl; auto fn = bind(less<int>(), _1, 4); cout << count_if(vec.begin(), vec.end(), fn); // 小于4的有几个 4个 cout << "\nbind sort" << endl; sort(vec.begin(), vec.end(), less<int>()); print<int>(vec); // 模板函数 sort(vec.begin(), vec.end(), bind(less<int>(), _2, _1)); print<int>(vec); } } int main() { // luck_count::test_count(); // cout << endl; // luck_find:: test(); // cout << endl; // luck_sort:: test(); luck_bind::test(); return 0; }