注:该课程大纲详见 https://www.cnblogs.com/inchbyinch/p/12398921.html
C++语言的核心优势之一就是便于软件的重用,C++中有两个方面体现重用:
标准模板库(英文:Standard Template Library,缩写:STL),是一个C++软件库,主要包含4个组件,分别为容器、迭代器、算法、函数。简单来说,STL就是一些常用数据结构和算法的模板的集合。
本文先介绍STL概要和函数对象,STL(下)中再详细介绍关联容器、容器适配器和算法。
可以用于存放各种类型的数据(基本类型的变量,对象等)的数据结构,都是类模版,分为三种:
对象被插入容器中时,被插入的是对象的一个复制品。许多算法,比如排序,查找,要求对容器中的元素进行比较,有的容器本身就是排序的,所以,放入容器的对象所属的类,往往还应该重载 == 和 < 运算符。
内部采用动态数组,头文件 < vector >,元素在内存连续存放。随机存取任何元素都能在常数时间完成。在尾端增删元素具有较佳的性能(大部分情况下是常数时间,除非需要二倍地扩充空间)。
//vector的初始化
vector<int> v1;
vector<string> v2;
vector<int> v3(v1); //用v1初始化
vector<int> v4(v1.begin(),v1.end()) //用v1的部分来初始化
vector<int> v5(10); //创建一个大小为10的vector,默认初始化为0
vector<string> v6(4); //默认初始化为空字符串
vector<int> v7(5, -1); //第一个参数是数目,第二个参数是要初始化的值
vector<string> v8(3, "hi");
vector<int> v9 = { 1,2,3,4,5 }; //列表初始化,注意使用的是花括号
vector<string> v10 = { "hi","my","name","is","lee" };
vector<vector<int> > v(10); //创建二维数组,注意保留空格
//示例
template<class T>
void PrintVector( T s, T e){
for(; s != e; ++s)
cout << * s << " ";
cout << endl;
}
int main(){
int a[5] = { 1,2,3,4,5 };
vector<int> v(a,a+5); //将数组a的内容放入v
cout << "1) " << v.end() - v.begin() << endl;
//两个随机迭代器可以相减,输出 1) 5
cout << "2) "; PrintVector(v.begin(),v.end());
//2) 1 2 3 4 5
v.insert(v.begin() + 2, 13); //在begin()+2位置插入 13
cout << "3) "; PrintVector(v.begin(),v.end());
//3) 1 2 13 3 4 5
v.erase(v.begin() + 2); //删除位于 begin() + 2的元素
cout << "4) "; PrintVector(v.begin(),v.end());
//4) 1 2 3 4 5
vector<int> v2(4,100); //v2 有4个元素,都是100
v2.insert(v2.begin(),v.begin()+ 1,v.begin()+3);
//将v的一段插入v2开头
cout << "5) v2: "; PrintVector(v2.begin(),v2.end());
//5) v2: 2 3 100 100 100 100
v.erase(v.begin() + 1, v.begin() + 3);
//删除 v 上的一个区间,即 2,3
cout << "6) "; PrintVector(v.begin(),v.end());
//6) 1 4 5
return 0;
}
注意:
内部采用双向队列,头文件 < deque >,元素在内存连续存放。随机存取任何元素都能在常数时间完成(但次于vector)。在两端增删元素具有较佳的性能(大部分情况下是常数时间,除非需要扩充空间)。
操作上:
内部采用双向链表,头文件 < list >,元素在内存不连续存放。在任何位置增删元素都能在常数时间完成。不支持随机存取。
除了具有所有顺序容器都有的成员函数以外,还支持8个成员函数:
//示例
class A { //自定义一个类
int n;
public:
A( int n_ ) { n = n_; }
friend bool operator<(const A& a1, const A& a2){ return a1.n < a2.n; }
friend bool operator==(const A& a1, const A& a2){ return a1.n == a2.n; }
friend ostream & operator <<(ostream& o, const A& a){
o << a.n;
return o;
}
};
//打印链表的模板。形参这里不推荐,最好用两个迭代器
template <class T>
void PrintList(const list<T> & lst) {
int tmp = lst.size();
if( tmp > 0 ) {
typename list<T>::const_iterator i;
i = lst.begin();
for( i = lst.begin();i != lst.end(); i++)
cout << * i << ",";
}
}// typename用来说明 list<T>::const_iterator是个类型, 在vs中不写也可以
int main() {
list<A> lst1,lst2;
lst1.push_back(1); lst1.push_back(3); lst1.push_back(2);
lst1.push_back(4); lst1.push_back(2);
lst2.push_back(10); lst2.push_front(20); lst2.push_back(30);
lst2.push_back(30); lst2.push_back(30);
lst2.push_front(40); lst2.push_back(40);
cout << "1) "; PrintList( lst1); cout << endl;
// 1) 1,3,2,4,2,
cout << "2) "; PrintList( lst2); cout << endl;
// 2) 40,20,10,30,30,30,40,
lst2.sort();
cout << "3) "; PrintList( lst2); cout << endl;
//3) 10,20,30,30,30,40,40,
lst2.pop_front();
cout << "4) "; PrintList( lst2); cout << endl;
//4) 20,30,30,30,40,40,
lst1.remove(2); //删除所有和A(2)相等的元素
cout << "5) "; PrintList( lst1); cout << endl;
//5) 1,3,4,
lst2.unique(); //删除所有和前一个元素相等的元素,需有序
cout << "6) "; PrintList( lst2); cout << endl;
//6) 20,30,40,
lst1.merge (lst2); //合并 lst2到lst1并清空lst2
cout << "7) "; PrintList( lst1); cout << endl;
//7) 1,3,4,20,30,40,
cout << "8) "; PrintList( lst2); cout << endl;
//8)
lst1.reverse();
cout << "9) "; PrintList( lst1); cout << endl;
//9) 40,30,20,4,3,1,
lst2.push_back (100);lst2.push_back (200);
lst2.push_back (300);lst2.push_back (400);
list<A>::iterator p1,p2,p3;
p1 = find(lst1.begin(),lst1.end(),3);
p2 = find(lst2.begin(),lst2.end(),200);
p3 = find(lst2.begin(),lst2.end(),400);
lst1.splice(p1,lst2,p2, p3);
//将[p2,p3)插入p1之前,并从lst2中删除[p2,p3)
cout << "10) "; PrintList( lst1); cout << endl;
//10) 40,30,20,4,200,300,3,1,
cout << "11) "; PrintList( lst2); cout << endl;
//11) 100,400,
return 0;
}
元素是排序的,插入任何元素,都按相应的排序规则来确定其位置,便于查找。内部通常以平衡二叉树方式实现,插入和检索的时间都是O(log(N))。
顺序容器和关联容器中都有的成员函数:
顺序容器的常用成员函数:
迭代器上可以执行++操作,以使其指向容器中的下一个元素。如果迭代器到达了容器中的最后一个元素的后面,此时再使用它,就会出错,类似于使用NULL或未初始化的指针一样。
//定义一个普通迭代器
容器类名::iterator 变量名;
//定义一个const迭代器
容器类名::const_iterator 变量名;
//访问一个迭代器指向的元素:
*迭代器变量名
若p和p1都是双向迭代器,则可对p、p1可进行以下操作:
若p和p1都是随机访问迭代器,则可对p、p1可进行以下操作:
//随机访问迭代器,下面三种遍历方法均可;而双向迭代器则只能用最后一种
vector<int> v(100);
int i;
for(i = 0; i < v.size(); i++) //根据下标随机访问
cout << v[i];
vector<int>::const_iterator ii;
for( ii = v.begin(); ii < v.end(); ++ii) //根据大小比较符号
cout << * ii;
for( ii = v.begin(); ii != v.end ();++ii) //根据判等符号
cout << * ii;
容器 | vector | deque | list | set/multiset | map/multimap | 容器适配器 |
---|---|---|---|---|---|---|
迭代器类别 | 随机 | 随机 | 双向 | 双向 | 双向 | 不支持迭代器 |
注意:有的算法,例如sort,binary_search需要通过随机访问迭代器来访问容器中的元素,那么list以及关联容器就不支持该算法!
STL中“大”、“小”的概念:
STL中“相等”的概念:
可参考:
函数指针是指向函数的指针变量,主要有两个作用:用作调用函数和做函数的参数。
//示例:将函数指针作为参数
typedef void (* PFT) ( char ,int );
void bar(char ch, int i){
cout<<"bar "<<ch<<' '<<i<<endl;
return ;
}
void foo(char ch, int i, PFT pf){
pf(ch,i);
return ;
}
PFT pft;
pft = bar;
foo('e', 12, pft);
上述例子我们首先利用一个函数指针pft指向bar(),然后在foo()函数中使用pft指针来调用bar(),实现目的。将这个特点稍加利用,我们就可以构造出强大的程序,只需要同样的foo函数便可以实现对不同bar函数的调用。
一个函数对象,即一个重载了括号操作符"()"的对象。当用该对象调用此操作符时,其表现形式如同普通函数调用一般,因此取名叫函数对象。
从一般的函数回调意义上来说,函数对象和函数指针是相同的,但是函数对象却具有许多函数指针不具有的特点,使程序设计更加灵活和强大。主要有两个优势:
//示例1:简单案例
class FuncT{
public:
template<typename T>
T operator() (T t1, T t2){
cout << t1 << '+' << t2 << '=' << t1+t2 <<endl;
return t1;
}
};
template <typename T>
T addFuncT(T t1, T t2, FuncT& funct){
funct(t1, t2);
return t1;
}
FuncT funct;
addFuncT(2, 4, funct);
addFuncT(1.4, 2.3, funct);
//示例2:函数对象在STL中accumulate模板上的应用
//STL中accumulate模板
//对[first,last)中的每个迭代器I,执行val= pr(val,*I),返回最终的val
//pr是个函数对象,Pr也可以是个函数。
template<class InIt, class T, class Pred>
T accumulate(InIt first, InIt last, T val, Pred pr);
//Dev C++ 中的 Accumulate 源代码1:
template<typename _InputIterator, typename _Tp> //typename等效于class
_Tp accumulate(_InputIterator __first, _InputIterator __last, _Tp __init){
for ( ; __first != __last; ++__first)
__init = __init + *__first;
return __init;
}
//Dev C++ 中的 Accumulate 源代码2:
template<typename _InputIterator, typename _Tp, typename _BinaryOperation>
_Tp accumulate(_InputIterator __first, _InputIterator __last,
_Tp __init, _BinaryOperation __binary_op){
for ( ; __first != __last; ++__first)
__init = __binary_op(__init, *__first);
return __init;
}
//主函数
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <functional>
using namespace std;
int SumSquares(int total, int value){ return total + value * value; }
template <class T>
void PrintInterval(T first, T last){ //输出区间[first,last)中的元素
for( ; first != last; ++ first)
cout << * first << " ";
cout << endl;
}
template<class T>
class SumPowers{
int power;
public:
SumPowers(int p) : power(p) { }
//计算value的power次方,加到total上
const T operator() (const T & total, const T & value){
T v = value;
for( int i = 0; i < power - 1; ++ i)
v = v * value;
return total + v;
}
};
int main(){
const int SIZE = 3;
int a1[] = { 1,2,3 };
vector<int> v(a1, a1+SIZE);
cout << "1) 原数据:"; PrintInterval(v.begin(), v.end());
int result = accumulate(v.begin(), v.end(), 0, SumSquares);
cout << "2) 平方和:" << result << endl;
result = accumulate(v.begin(), v.end(), 0, SumPowers<int>(3));
cout << "3) 立方和:" << result << endl;
result = accumulate(v.begin(), v.end(), 0, SumPowers<int>(4));
cout << "4) 4次方和:" << result;
return 0;
}
对示例2的分析:
int accumulate(vector<int>::iterator first, vector<int>::iterator last,
int init, int ( * op)(int, int)){
for ( ; first != last; ++first)
init = op(init, *first);
return init;
}
int accumulate(vector<int>::iterator first, vector<int>::iterator last,
int init, SumPowers<int> op){
for ( ; first != last; ++first)
init = op(init, *first);
return init;
}
应用:< functional > 里还有以下函数对象类模板,均可用来生成函数对象:
注意:
//002:按距离排序 http://cxsjsxmooc.openjudge.cn/2019t3fall8/002/
//要求自定义sort的比较器,即Closer类的函数对象,使得数据按照离指定值的距离大小排序
#include <iostream>
#include <cmath>
#include <algorithm>
#include <string>
using namespace std;
/*************code start here****************/
template <class T1, class T2>
struct Closer {
T1 v;
T2 dis;
Closer(T1 t, T2 op) : v(t), dis(op){ }
bool operator()(const T1& t1, const T1& t2){
int d1 = dis(t1, v);
int d2 = dis(t2, v);
if(d1 < d2)
return true;
else if(d1 > d2)
return false;
else
return t1 < t2;
}
};
/*************code end here****************/
int Distance1(const int n1, const int n2) {
return abs(n1-n2);
}
int Distance2(const string & s1, const string & s2) {
return abs((int)s1.length()- (int)s2.length());
}
int a[10] = { 0,3,1,4,7,9,20,8,10,15};
string b[6] = {"American","Jack","To","Peking","abcdefghijklmnop","123456789"};
int main(){
int n;
string s;
while(cin >> n >> s) {
sort(a, a+10, Closer<int, int(*)(int, int)>(n,Distance1));
for(int i = 0; i < 10; ++i)
cout << a[i] << "," ;
cout << endl;
sort(b, b+6, Closer<string, int(*)(const string&, const string&)>(s, Distance2));
for(int i = 0; i < 6; ++i)
cout << b[i] << "," ;
cout << endl;
}
return 0;
}
//006:我自己的ostream_iterator http://cxsjsxmooc.openjudge.cn/2019t3fall8/006/
//程序填空,使得输出为:
//5,21,14,2,3,[换行]1.4--5.56--3.2--98.3--3.3--
template <class T1,class T2>
void Copy(T1 s, T1 e, T2 x){
for(; s != e; ++s,++x)
*x = *s;
}
/************code start here***************/
template<class T>
class myostream_iterator{
ostream& o;
string s;
T val;
public:
myostream_iterator(ostream& oo, const char* ss) : o(oo), s(ss){ }
T& operator*(){ return val; }
void operator++(){ o << val << s; }
};
/************code end here***************/
int main(){
const int SIZE = 5;
int a[SIZE] = {5,21,14,2,3};
double b[SIZE] = { 1.4, 5.56,3.2,98.3,3.3};
list<int> lst(a,a+SIZE);
myostream_iterator<int> output(cout,",");
Copy( lst.begin(),lst.end(),output);
cout << endl;
myostream_iterator<double> output2(cout,"--");
Copy(b,b+SIZE,output2);
return 0;
}
原文:https://www.cnblogs.com/inchbyinch/p/12398639.html