C++中有两方面体现重用:1、面向对象的思想:继承和多态,标准类库 2、泛型程序设计的思想:模板机制,以及标准模板库STL
标准模板库(Standard Template Library)就是一些常用数据结构和算法的模板的集合,有了STL,不必再写太多的标准数据结构和算法,并且可以获得非常高的性能
STL六大部件:容器(Containers)、分配器(Allocators)、算法(Alogrithms)、迭代器(Iterators)、仿函数(Functors,头文件为<functional>)、适配器(转换器)(Adapters)
一、容器(Containers)
容器的定义:
在数据存储上,有一种对象类型,它可以持有其它对象或指向其它对象的指针,这种对象类型就叫做容器。很简单,容器就是保存其它对象的对象,当然这是一个朴素的理解,这种“对象”还包含了一系列处理“其它对象”的方法。容器即物之所在,容器是STL的核心部件之一,是迭代器的依附,是算法作用的目标。
容器的种类:
顺序容器:是一种各元素之间有顺序关系的线性表,是一种线性结构的可序群集。顺序性容器中的每个元素均有固定的位置,除非用删除或插入的操作改变这个位置。顺序容器的元素排列次序与元素值无关,而是由元素添加到容器里的次序决定。顺序容器包括:vector(向量)、list(列表)、deque(队列)。
关联容器:关联式容器是非线性的树结构,更准确的说是二叉树结构。各元素之间没有严格的物理上的顺序关系,也就是说元素在容器中并没有保存元素置入容器时的逻辑顺序。但是关联式容器提供了另一种根据元素特点排序的功能,这样迭代器就能根据元素的特点“顺序地”获取元素。元素是有序的集合,默认在插入的时候按升序排列。关联容器包括:map(集合)、set(映射)、multimap(多重集合)、multiset(多重映射)。
容器适配器:本质上,适配器是使一种不同的行为类似于另一事物的行为的一种机制。容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。适配器是容器的接口,它本身不能直接保存元素,它保存元素的机制是调用另一种顺序容器去实现,即可以把适配器看作“它保存一个容器,这个容器再保存所有元素”。STL 中包含三种适配器:栈stack 、队列queue 和优先级队列priority_queue 。
容器类自动申请和释放内存,因此无需new和delete操作。
不同容器的使用方法:
1、c++语言中,multiset 是 <set> 库中一个非常有用的类型,它可以看成一个序列,插入一个数,删除一个数都能够在O(logn)的时间内完成,而且他能时刻保证序列中的数是有序的,而且序列中可以存在重复的数
/* 输入n个整数,输出整数数列中大小排名前k的偶数 测试用例: 2 9 4 1 2 4 3 6 6 7 8 9 3 2 18 16 14 */ #include <iostream> #include <set> using namespace std; class MyQueue { int k; //创建对象时,k表示前k个偶数整数 multiset < int, greater<int> > que; //multiset容器对象 元素有序且可以重复 public: MyQueue(int k):k(k) {} //容器插入对象 friend istream & operator>>(istream&is, MyQueue &a) //对象输入 重载 >> 运算符 { int num; is >> num; if (num % 2 == 0) { a.que.insert(num); //是偶数插入 } return is; } //从容器输出对象 friend ostream & operator <<(ostream&os, MyQueue &a) //对象输出 重载 << 运算符 { multiset<int>::iterator p = a.que.begin(); //迭代器 int count = 0; for (; count < a.k; p++) { if (count){ os << " "; } os << *p ; //输出元素 count++; //记录输出元素个数 } return os; } }; int main() { int t; cin >> t; while(t--) { int n, k; cin >> n >> k; MyQueue q(k); for (int i = 0; i < n; ++i) cin >> q; //利用>>运算符重载 输入整数(插入到对象 q 的 multiset 容器中) cout<<q; cout << endl; } return 0; }
2、<vector> 容器的 for_each(iterator,iterator,pointer)(第三个参数:pointer是指向函数的指针:全局函数 或 函数对象-->临时对象)
临时对象,就是一种无名对象(unamed objects),临时对象:类名之后直接加一对小括号 () 或者大括号 {}(列表初始化),并可指定初值,其意义相当于调用相应的构造函数并且不指定对象名
函数对象(function object),形式上是一个类实例化的对象,本质上是为了实现某种与函数等价的功能,函数对象的思想是:用类来封装一个函数功能,然后用这个类实例化不同的函数对象,若一个类重载了运算符(),则该类的对象就成为函数对象
/* 完成以下程序,使得输入的整数x,以及若干正整数,将 大于x的正整数输出;然后输入若干字符串,将字符串长度大于x的字符串输出 测试用例: 2 5 6 1 3 59 30 2 40 this is hello please me ha 1 1 4 this 输出: 59,30,40, please, 4, this, */ #include <algorithm> //for_each 头文件 #include <iostream> #include <vector> using namespace std; class Printer { int size; public: Printer(int x) : size(x) {} void operator()(int x) //运算符()重载 { if (x > size) cout << x << ','; } void operator()(string str) //运算符()重载 { if (str.size() > size) cout << str << ','; } }; int main() { int t; cin >> t; while(t--) { int n,x; cin >> x >> n; vector<int> intVec; for(int i = 0; i < n; ++i) { int y; cin >> y; intVec.push_back(y); } for_each(intVec.begin(), intVec.end(), Printer(x)); //Printer(x)是函数对象 cout << endl; vector<string> strVec; for(int i = 0; i < n; ++i) { string str; cin >> str; strVec.push_back(str); } for_each(strVec.begin(), strVec.end(), Printer(x)); cout << endl; } return 0; }
全局函数:
#include <iostream> #include <vector> #include <algorithm> //for_each using namespace std; //全局函数 void printElem(int elem, const char* prefix) { cout << prefix << elem << endl; } int main() { int ia[] = {1, 2, 3}; vector<int> ivec(ia, ia + sizeof(ia) / sizeof(int)); for_each(ivec.begin(), ivec.end(), bind2nd(ptr_fun(printElem), "Element:")); return 0; }
3、<list> 容器的 sort 参数:< 运算符重载,函数对象(重载 () 运算符),全局函数,<list>容器的 for_each 的参数为全局函数
/* 三生三世 测试用例:3代表3个电视剧,以下是 每个电视剧的剧名 及 演员阵容、剧情和演技的分数 3 In the Name of People 98 97 99 Life After Life, Blooms Over Blooms 99 82 73 Ming Dynasty: 1566 97 100 100 输出:按分数(分别为演员阵容、剧情和演技的分数)从大到小排序3次,并按排序输出剧名 */ #include <iostream> //string #include <list> //list #include <algorithm> // sort using namespace std; class TV_Drama { public: char name[100]; //电视剧 int actor; //演员阵容评分 int story; //剧情评分 int acting_skill; //演技评分 // 在此处补充你的代码 TV_Drama(string _name, int _actor, int _story, int _acting_skill) : actor(_actor), story(_story), acting_skill(_acting_skill) { _name.copy(name, _name.size(), 0); //string类的函数 name[_name.size()] = 0; //字符串结束符 name 电视剧剧名 } bool operator <(const TV_Drama &obj) { //对象比较,重载比较运算符 < return actor > obj.actor; //从高到低排序 } }; void Printer(const TV_Drama &obj) { //全局函数 cout << obj.name << ';'; //cout <<"--" << obj.actor << "--" << obj.story << "--" << obj.acting_skill<< endl; } bool comparator_1(const TV_Drama &o1, const TV_Drama &o2) { //全局函数 return o1.story > o2.story; //从高到低排序 } class comparator_2 { //函数对象 ,重载了小括号 () 运算符 public: bool operator()(const TV_Drama &o1, const TV_Drama &o2) { return o1.acting_skill > o2.acting_skill; //从高到低排序 } }; int main() { list<TV_Drama> lst; //对象容器 int n; cin >> n; char _name[100]; int _actor, _story, _acting_skill; for (int i = 0; i < n; i++) { cin.ignore(); //cin.ignore()函数用于忽略或清除输入缓冲区中的一个或多个字符 cin.getline(_name, 100); cin >> _actor >> _story >> _acting_skill; lst.push_back(TV_Drama(_name, _actor, _story, _acting_skill)); } lst.sort(); //无参 默认对象从小到大,重载 < 运算符改为 从大到小 for_each(lst.begin(), lst.end(), Printer); cout << endl; lst.sort(comparator_1); //全局函数 for_each(lst.begin(), lst.end(), Printer); cout << endl; lst.sort(comparator_2()); //函数对象 for_each(lst.begin(), lst.end(), Printer); cout << endl; return 0; }
4、<map> 容器,单词统计
#include <iostream> #include <map> #include <string> #include <algorithm> using namespace std; /*单词计数器 */ string& strip(string &str) { for (auto &ch : str) ch = tolower(ch);// 转成小写 str.erase( remove_if ( str.begin(), str.end(), static_cast<int(*)(int)>(&ispunct) ), str.end()); return str; }; std::map<std::string, std::size_t> count() { std::map<std::string, std::size_t> counts; for (string w; cin >> w; ++counts[strip(w)]) ; return counts; } void println(std::map<std::string, std::size_t> const &m) { for (auto const &kv : m) { cout << kv.first << " -- " << kv.second << endl; } } int main() { println(count()); //count()函数返回的是map容器 cin.clear(); return 0; }