文章目录
1. string类
string
类中预定义了大量关于字符串的操作,在使用该类时需要引入头文件#include<string>
。几种常用的字符串初始化方法包括:
// 正确的初始化方法
string s1("Hello");
string s2 = "Hello";
string s3(3, 'a'); // s3初始化为3个连续字符'a'组成的字符串
// 错误的初始化方法
string s4 = 'a';
string s5('a');
string s6=11;
string s7(11);
除了以上列举的正确和错误的字符串初始化方法,还存在一种特殊的情况,即将字符赋值给string
对象:
string s;
s = 'n';
最后列举出string
类的特性以及常用函数:
- 使用
length
函数获取string
类对象的长度; string
支持流读取运算符,即cin >> stringObject;
;string
支持getline()
函数,即getline(cin, str);
;- 两个
string
类之间可以使用等号赋值; - 可以使用成员函数
assign()
实现对象的整体或部分赋值; string
对象的访问类似于数组,即使用[]
访问,同时也可以使用成员函数at()
实现元素访问;- 可以仅使用加号将两个
string
类对象连接,也可以使用成员函数append()
实现该功能; - 可以使用关系运算符比较两个
string
类对象的大小,比较依据是字典序,也可以使用成员函数compare()
实现该功能; - 成员函数
substr()
可以取string
类对象的子串; - 成员函数
swap()
可以交换两个string
类对象的内容; - 成员函数
find()
用于查找string
类对象中的子串,如果找到则返回所在位置的索引; - 成员函数
rfind()
的功能同find()
,但二者的查找方向相反; - 成员函数
erase()
用于删除string
类对象中的字符; - 成员函数
replace()
用于替换string
类对象中的字符; - 成员函数
insert()
用于在string
类对象中插入字符; - 成员函数
c_str()
用于将string
类对象转化成char*
。
2. STL,标准模板库简介
标准模板库是 C + + {\rm C++} C++中的重要内容,前面提到 C + + {\rm C++} C++语言的核心优势之一是便于软件重用,这主要体现在两个重要的方面:
- 面向对象的思想,包括继承、多态,标准类库等;
- 泛型程序设计的思想,包括模板机制以及标准模板库等。
在标准模板库中, C + + {\rm C++} C++将一些常用的数据结构,如链表、数组、二叉树等,以及一些常用的算法,如排序、查找等写成模板以方便我们使用。而以后无论数据结构里存放的是什么类型的数据,算法作用的对象是什么,我们都不必去重写数据结构而可以直接使用标准模板库的内容。 S T L {\rm STL} STL中包含三个基本的概念,分别是容器、迭代器和算法:
- 容器 可以容纳各种数据类型的通用数据结构,是类模板。其中顺序容器包括vector、deque、list,关联容器包括set、multiset、map、multimap,容器适配器包括stack、queue、priority_queue;
- 迭代器 可以用于依次存取容器中的元素,类似于指针;
- 算法 用来操作容器中的元素的函数模板,如
sort()
函数和find()
函数等。而算法本身与它们所操作的数据结构无关,因此它们可以在从简单数组到复杂容器的任何数据结构上使用。
3. 常用的STL
首先介绍顺序容器,它的元素存放位置是无序的
3.1 vector
vector包含在头文件#include<vector>
中,它表示一种动态数组,元素在内存中连续存放。它具备数组的一般特性,即随机存取任何元素都能在常数时间完成。程序实例:
#include<iostream>
#include<vector>
using namespace std;
// 定义函数模板,将vector里的元素全部输出
template<class T> void PrintVector(T s, T e) {
for (;s != e; ++s) {
cout << *s << " ";
}
}
int main() {
int a[5] = {1, 2, 3, 4, 5};
vector<int> v(a, a+5); // 将数组a的元素赋值给vector v
cout << v.end() - v.begin() << " "; // 输出5
v.insert(v.begin() + 2, 13); // 将13插入第2个位置,vector变为{1, 2, 13, 3, 4, 5}
v.erase(v.begin() + 2); // 删除第2个位置处的元素,vector变为{1, 2, 3, 4, 5}
vector<int> v2(4, 100); // vector v2一共有四个元素,全部为100
v2.insert(v2.begin(), v.begin() + 1, v.begin() + 3); // 将v中的一段元素插入v2开头处
v.erase(v.begin() + 1, v.begin() + 3); // 删除v中的一段元素
PrintVector(v.begin(), b.end()); // 打印
return 0;
}
vector
还可用于定义二维数组,即vector<vector<int>> v(3);
表示定义了一个二维数组,
3
3
3表示定义了含有三个元素vector<int>
的vector
,即二维向量。示例程序:
#include<iostream>
#include<vector>
using namespace std;
int main() {
// 二维vector
vector<vector<int>> v(3);
// 往二维vector内添加元素
for (int i = 0; i < v.size(); ++i) {
for (int j = 0; j < 4; ++j) {
v[i].push_back(j);
}
}
// 输出四行的0 1 2 3
for (int i = 0; i < v.size(); ++i) {
for (int j = 0; j < v[i].size(); ++j) {
cout << v[i][j] << " ";
}
cout << endl;
}
}
3.2 deque
deque包含在头文件#include<deque>
中,它表示一种双向队列,元素在内存中连续存放。随机存取任何元素都能在常数时间完成。这里,所有适用于vector
的操作均适用于deque
。除此之外,deque
还有将元素插入到最前面以及删除最前面元素的操作,且时间复杂度是
O
(
1
)
O(1)
O(1)。
3.3 list
list包含在头文件#include<list>
中,它表示一种双向链表,元素在内存中不连续存放。在任何位置增删元素都能在常数时间完成,不支持随机存取。除了具有所有顺序容器都有的成员函数外,还支持以下函数:
push_front
在链表的最前面插入;pop_front
删除最前面的元素;sort
排序(但不支持 S T L {\rm STL} STL中的排序函数);remove
删除和指定值相等的所有元素;unique
删除所有和前一个元素相同的元素(如果需要删除容器中的全部重复元素,应先执行排序操作);merge
合并两个链表;reverse
翻转链表;splice
在指定位置前面插入另一个链表中的一个或多个元素,并在另一个链表中删除被插入的元素。
示例程序:
#include<list>
#include<iostream>
#include<algorithm>
using namespace std;
class A {
private:
int n;
public:
// 构造函数
A(int n_) {
n = n_;
}
// 友元函数
friend bool operator<(const A& a1, const A& a2);
friend bool operator==(const A& a1, const A& a2);
friend ostream& operator<<(ostream& o, const A& a);
};
bool operator<(const A& a1, const A& a2)
{
return a1.n < a2.n;
}
bool operator==(const A& a1, const A& a2)
{
return a1.n == a2.n;
}
ostream& operator<<(ostream& o, const A& a)
{
o << a.n;
return o;
}
// 打印链表值的模板
template<class T> void PrintList(const list<T>& lst) {
// typename关键字
typename list<T>::const_iterator i;
i = lst.begin();
for (; i != lst.end(); ++i) {
cout << *i << ",";
}
}
int main() {
// 链表的元素均为A的对象
list<A> lst1, lst2;
// lst1: 1->3->2->4->2
lst1.push_back(1); lst1.push_back(3);
lst1.push_back(2); lst1.push_back(4);
lst1.push_back(2);
// lst2: 40->20->10->30->30->30->40
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);
// 排序,lst2: 10->20->30->30->30->40->40
lst2.sort();
// lst2: 20->30->30->30->40->40
lst2.pop_front();
// 删除所有和A(2)相等的元素,lst1: 1->3->4
lst1.remove(2);
// 删除重复元素,lst2: 20->30->40
lst2.unique();
// 合并,并清空lst2,lst1: 1->3->4->20->30->40
lst1.merge(lst2);
// 翻转链表,lst1: 40->30->20->4->3->1
lst1.reverse();
// 往lst2中添加元素
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(lst1.begin(), lst1.end(), 200);
p3 = find(lst1.begin(), lst1.end(), 400);
// 在lst2中将[p2,p3)元素插入p1位置,lst1:lst1: 40->30->20->4->200->300->3->1
lst1.splice(p1, lst2, p2, p3);
return 0;
}
其次是关联容器,容器中的元素是排序的,插入任何元素都按相应的排序规则来确定其位置,在查找时具有非常好的性能。该类容器通常以平衡二叉树实现,插入和检索的时间复杂度都是O(logN)
3.4 set / multiset
set / multiset包含在头文件#include<set>
中,set中不允许包含相同的元素,而multiset中允许存在相同的元素。关联容器除了各容器都有的函数外,还支持以下成员函数:
find
查找等于某个值的元素(x
小于y
和y
小于x
同时不成立即为相等);lower_bound
:查找某个下界;upper_bound
:查找某个下界;euqal_range
:同时查找上界和下界;count
:计算等于某个值的元素,等于的定义同上;insert
:用以插入一个元素或一个区间。
multiset
template<class Key, class Pred=less<Key>, class A=allocator<Key>>
Pred
类型的变量决定了multiset
中的元素,即比较大小是怎么定义的。less
的定义:
template<class T> struct less: public binary_function<T, T, bool> {
bool operator()(const T& x, const T& y) {
return x < y; // 默认使用小于号比较大小
}
const;
};
multiset 的成员函数:
iterator find(const T& val);
在容器中查找值为val
的元素,返回其迭代器;如果找不到,则返回end()
;iterator insert(const T& val);
将val
插入到容器中并返回其迭代器;void insert(iterator first, iterator last);
将区间[first, last)
插入容器;int count(const T&val);
统计有多少个元素的值和val
相等;iterator lower_bound(const T& val);
查找一个最大的位置it
,使得[begin(), it)
中的所有元素均比val
小;iterator upper_bound(const T& val);
查找一个最小的位置it
,使得[it, end())
中的所有元素均比val
大;pair<iterator, iterator> equal_range(const T& val);
同时求得lower_bound
和upper_bound
;iterator erase(iterator it);
删除it
指向的元素,返回其后面元素的迭代器。
错误示例程序:
#include<set>
using namespace std;
class A {};
int main(){
multiset<A> a;
a.insert(A()); // 出错
}
在插入元素时,multiset
会将被插入元素和已有元素进行比较。由于默认是用小于号比较的,所以这要求上述类A
的对象能够使用小于号作比较,但是在该类中小于号没有被重载,所以会出错。示例程序:
#include<iostream>
#include<set>
using namespace std;
// 打印函数模板
template<class T> void Print(T first, T last) {
for (; first != last; ++first) {
cout << *first << " ";
}
cout << endl;
}
// 类A
class A {
private:
int n;
public:
// 构造函数
A(int n_) {
n = n_;
}
// 重载
friend bool operator < (const A& a1, const A& a2) {
return a1.n < a2.n;
}
friend ostream& operator<<(ostream& o, const A& a2) {
o << a2.n;
return o;
}
// 友元类
friend class MyLess;
};
struct MyLess {
// 类对象为函数对象
bool operator()(const A& a1, const A& a2) const {
// 个位较小者小
return (a1.n % 10) < (a2.n % 10);
}
};
typedef multiset<A> MSET1; // MSET1用小于号比较大小
typedef multiset<A, MyLess> MSET2; // MSET2用MyLess比较大小
int main() {
const int SIZE = 6;
A a[SIZE] = { 4,22,19,8,33,40 };
MSET1 m1;
// 4 8 19 22 33 40
m1.insert(a, a + SIZE);
// 4 8 19 22 22 33 40
m1.insert(22);
// 22
cout << *m1.lower_bound(22) << " ";
// 33
cout << *m1.upper_bound(22) << " ";
// 4 8 19 33 40
m1.erase(m1.lower_bound(22), m1.upper_bound(22));
// MSET2对象
MSET2 m2;
// 40 22 33 4 8 19
m2.insert(a, a + SIZE);
return 0;
}
set的示例程序:
#include<iostream>
#include<set>
using namespace std;
int main() {
typedef set<int>::iterator IT;
int a[5] = {3, 4, 6, 1, 2};
// 1 2 3 4 6
set<int> st(a, a + 5);
pair<IT, bool> result;
// 1 2 3 4 5 6
result = st.inset(5);
// 判断是否插入成功
// 插入成功
if (result.second) {
// ...
}
// 插入失败
else {
// ...
}
pair<IT, IT> bounds = st.equal_range(4);
// 45
cout << *bounds.first << *bounds.second;
return 0;
}
3.5 map / multimap
map / multimap包含在头文件#include<set>
中,map中不允许包含相同的元素,而multimap中允许存在相同的元素。map与set的最大不同在于,前者存放的元素有且仅有两个成员变量,一个名为first
,另一个名为second
。map会根据first
的顺序对容器内的元素从小到大进行排序,并且可以通过first
快速检索元素。map / multimap容器里存放的都是pair
对象。
- map / multimap中的元素由
<关键字, 值>
组成,每个元素都是一个pair
对象,关键字是first
成员变量,其类型是Key
; - multimap中允许存在多个元素的关键字相同,元素按照
first
成员变量从小到大排列。
示例程序:
#include<iostream>
#include<map>
using namespace std;
int main(){
typedef multimap<int, double, less<int>> mmid;
mmid pairs;
// 0
cout << pairs.count(15) << " ";
pairs.insert(mmid::value_type(15, 2.7));
pairs.insert(mmid::value_type(15, 99.3));
// 2
cout << pairs.count(15) << " ";
pairs.insert(mmid::value_type(30, 111.11));
pairs.insert(mmid::value_type(10, 22.22));
pairs.insert(mmid::value_type(25, 33.333));
pairs.insert(mmid::value_type(20, 9.3));
// 10,22.22 15,2.7 15,99.3 20,9.3 25,33.333 30,111.11
for (mmid::const_iterator i = pairs.begin(); i != pairs.end(); ++i) {
cout << i->first << "," << i->second << " ";
}
}
例题 学生成绩录入和查询系统,该系统的输入包含两种:第一种输入表示要添加一个学生的信息,碰到这种输入,就记下学生的姓名、学号和分数;第二种输入表示要查询,碰到这种输入,就输出已有记录中分数比输入分数低的最高分获得者的姓名、学号和分数。如果有多个学生满足条件,就输出学号最大的那个学生的信息。如果找不到满足条件的学生,则输出相应的提示信息。输入和输出样例:
// 输入
Add Jack 12 78 // 添加Jack,学号是12,成绩是78
Query 78 // 查询比78低的最高分获得者的姓名、学号和分数
Query 81
Add Percy 9 81
Add Marry 8 81
Query 82
Add Tom 11 79
Query 80
Query 81
// 输出
Nobody // 查询结果为空
Jack 12 78
Percy 9 81
Tom 11 79
Tom 11 79
示例程序:
#include<map>
#include<string>
#include<iostream>
using namespace std;
class CStudent {
public:
// 类的内部定义其他类
struct CInfo {
int id; // 学号
string name; // 姓名
};
int score; // 分数
CInfo info; // 学生的其他信息
};
// 第一个关键字是整型,第二个关键字是CInfo
typedef multimap<int, CStudent::CInfo> MAP_STD;
int main() {
MAP_STD mp;
CStudent st;
string cmd;
while (cin >> cmd)
{
// 添加信息
if (cmd == "Add") {
// 接收输入姓名、学号和分数
cin >> st.info.name >> st.info.id >> st.score;
// 插入
mp.insert(MAP_STD::value_type(st.score, st.info));
}
// 查询
else if (cmd == "Query") {
// 输入分数
int score;
cin >> score;
// 查询比score低的最大值
MAP_STD::iterator p = mp.lower_bound(score);
// 查找成功
if (p != mp.begin()) {
// 前一个元素
--p;
// 记录结果
score = p->first;
MAP_STD::iterator maxp = p;
int maxId = p->second.id;
// 循环查找具有最大学号的元素
for (; p != mp.begin() && p->first == score; --p) {
if (p->second.id > maxId) {
maxp = p;
maxId = p->second.id;
}
}
// 上述循环以p == mp.begin()时结束额外处理
if (p->first == score) {
if (p->second.id > maxId) {
maxp = p;
maxId = p->second.id;
}
}
// 输出
cout << maxp->second.name << " " << maxp->second.id << " " << maxp->first << endl;
}
// 查找失败
else
{
cout << "Nobody" << endl;
}
}
}
return 0;
}
与multimap相比,map不能存在相同的元素外,还具有一个重要的特性,即通过[ ]
取值,返回对关键字等于key
的元素的值的引用。如果没有该关键字,则会自动往map里面插入该关键字,并返回其引用。示例程序:
#include<map>
#include<iostream>
using namespace std;
// 重载左移运算符,输出pair对象的值
template<class Key, class Value> ostream& operator<<(ostream& o, const pair<Key, Value>& p) {
o << p.first << "," << p.second << " ";
return o;
}
int main() {
typedef map<int, double, less<int>> mmid;
mmid pairs;
// 0
cout << pairs.count(15) << " ";
pairs.insert(mmid::value_type(15, 2.7));
pairs.insert(make_pair(15, 99.4));
// 1
cout << pairs.count(15) << " ";
pairs.insert(mmid::value_type(20, 9.3)); // set不允许出现重复元素,插入失败
mmid::iterator i;
// 15,2.7 20,9.3
for (i = pairs.begin(); i !=pairs.end(); ++i) {
cout << *i << ",";
}
cout << endl;
// 没有40,插入该元素
int n = pairs[40];
// 15,2.7 20,9.3 40,0
for (i = pairs.begin(); i != pairs.end(); ++i) {
cout << *i << ",";
}
// 将关键字15对应的元素改成6.28
// 15,6.28 20,9.3 40,0
for (i = pairs.begin(); i != pairs.end(); ++i) {
cout << *i << ",";
}
return 0;
}
最后是容器适配器
3.6 stack
stack包含在头文件#include<stack>
中,它表示一种栈,是项的有限序列。并且满足序列中被删除、检索和修改的项只能是最近插入序列的项,是一种满足后进先出的数据结构。
push
插入元素;pop
弹出元素;top
返回栈顶元素的引用。
3.7 queue
queue包含在头文件#include<queue>
中,它表示一种队列,插入只可以在尾部进行,删除、检索和修改只允许从头部进行,是一种满足先进先出的数据结构。队列的成员函数与栈类似,遵循先进先出的原则。
3.8 priority_queue
priority_queue包含在头文件#include<queue>
中,它表示一种队列,最高优先级(通常是最大或最小)的元素总是位于队列头部。示例程序:
#include<queue>
#include<iostream>
using namespace std;
int main() {
priority_queue<double> pq1;
pq1.push(3.2); pq1.push(9.8); pq1.push(9.8); pq1.push(5.4);
// 9.8 9.8 5.4 3.2
while(!pq1.empty()) {
cout << pq1.top() << " ";
pq1.pop();
}
// 比较大小使用greater
priority_queue<double, vector<double>, greater<double>> pq2;
pq2.push(3.2); pq2.push(9.8); pq2.push(9.8); pq2.push(5.4);
// 3.2 5.4 9.8 9.8
while(!pq2.empty()) {
cout << pq1.top() << " ";
pq1.pop();
}
return 0;
}
3.9 顺序容器和关联容器中常用的成员函数
begin
返回指向容器中第一个元素的迭代器;end
返回指向容器中最后一个元素后面的位置的迭代器;rbegin
返回指向容器最后一个元素的迭代器;rend
返回指向容器中第一个元素前面的位置的迭代器;erase
从容器中删除一个或多个元素;clear
从容器中删除所有元素。
3.10 顺序容器中常用的成员函数
front
返回容器中第一个元素的索引;back
返回容器中最后一个元素的引用;push_back
在容器末尾增加新元素;pop_back
删除容器末尾的元素;erase
删除迭代器指向的元素,或删除一个区间,返回被删除元素后面那个元素的迭代器。
4. 迭代器
- 用于指向顺序容器和关联容器中的元素;
- 迭代器用法和指针类似;
- 有
const
和非const
两种; - 通过迭代器可以读取它指向的元素;
- 通过非
const
迭代器还能修改其指向的元素。
定义迭代的格式是容器类名::iterator 变量名;
或容器类名::const_iterator 变量名;
,使用以下方式访问一个迭代器指向的元素*迭代器变量名;
。此外,迭代上可以执行++
操作,以使其指向容器中的下一个元素。如果迭代器到达了容器中的最后一个元素的后面,此时再使用它,就会出错,类似于使用NULL
或未初始化的指针一样。
int main() {
vector<int> v;
v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4);
vector<int>::const_iterator i; // 常量迭代器
for(i = v.begin(); i != v.end(); ++i) {
cout << *i << " "; // 输出1 2 3 4
}
vector<int>::reverse_iterator r; // 反向迭代器
for(r= v.begin(); r != v.end(); ++r) {
cout << *r<< " "; // 输出4 3 2 1
}
vector<int>::iterator j; // 非常量迭代器
for(j= v.begin(); j != v.end(); ++j) {
*j = 100;
}
for(j= v.begin(); j != v.end(); ++j) {
cout << *j<< " "; // 输出100 100 100 100
}
return 0;
}
迭代器主要分为双向迭代器和随机访问迭代器。首先若p
和q
均是双向迭代器,则可对p
和q
作如下操作:
++p
,p++
,使p
指向容器中的下一个元素;--p
,p--
,使p
指向容器中的上一个元素;*p
,取p
指向的元素;p = q
,赋值操作;p == q
,p != q
,判断是否相等/不相等。
对于随机访问迭代,它的功能更加强大,除具备双向迭代器的所有功能外,它还具备如下操作:
p += i
,将p
向后移动i
个元素;p -= i
,将p
向前移动i
个元素;p + i
,指向p
后面的第i
个元素的迭代器;p - i
,指向p
前面的第i
个元素的迭代器;p[i]
,p
后面的第i
个元素的引用;p < q
,p <= q
,p > q
,p >= q
,比较大小。
5. 算法简介
S T L {\rm STL} STL中内置许多算法,其基本特性如下:
- 算法就是一个个函数模板,大多数都在头文件
#include<algorithm>
中定义; - S T L {\rm STL} STL中提供能在各种容器中通用的算法,比如查找、排序等;
- 算法通过迭代器来操纵容器中的元素。许多算法可以对容器中的一个局部区间进行操作,因此通常需要两个参数,一个是起始元素的迭代器,一个是终止元素的后面一个元素的迭代器;
- 有的算法返回一个迭代器。如查找算法在容器中查找一个元素,并返回一个指向该元素的迭代器;
- 算法可以处理容器,也可以处理普通数组。
int main() {
int array[10] = {10, 20, 30, 40};
vector<int> v;
v.push_back(1); v.push_back(2); v.push_back(3); v.push_back(4);
vector<int>::iterator p;
p = find(v.begin(), v.end(), 3);
if (p != v.end()) {
cout << *p << " "; // 查找成功,输出3
}
p = find(v.begin(), v.end(), 9);
if (p == v.end()) {
cout << "查找失败!" << " "; // 查找失败,输出“查找失败!”
}
p = find(v.begin() + 1, v.end() - 2, 1); // 在区间[2,3)内查找
if (p != v.end()) {
cout << *p << " "; // 查找成功,输出3
}
int *pp = find(array, array + 4, 20); // 对数组的查找
cout << *pp << " "; // 查找成功,输出20
return 0;
}
6. STL中的大小比较
- 关联容器内部的元素是从小到大排序的;
- 有些算法要求其操作的区间是从小到大排序的,称为有序区间算法,如二分查找等;
- 有些算法会对区间进行从小到大排序,称为排序算法,如排序等;
- 还有一些算法会用到大、小的概念。使用 S T L {\rm STL} STL时,在缺省的情况下,以下说法等价:x比y小,表达式x<y为真,y比x大;
- 有时,x和y相等等价于x==y为真,如顺序查找;
- 有时,x和y相等等价于x<y和y<x同时为假,如二分查找。
class A {
int v;
public:
A (int n): v(n) {} // 构造函数
bool operator<(const A& a2) const { // 重载小于符号,任何A对象不会存在一个小于另外一个的情况
cout << v << "<" << a2.v << "?" << endl;
return false;
}
bool operator==(const A& a2) const { // 重载等于符号
cout << v << "==" << a2.v << "?" << endl;
return v == a2.v;
}
};
int main() {
A a[] = {A(1), A(2), A(3), A(4), A(5)};
cout << binary_search(a, a + 4, A(9)); // 折半查找,返回1。这里用到的是x<y和x>y均不成立,则判断x==y
return 0;
}
7. 函数对象
函数对象的定义是:如果一个类重载了圆括号()
,则该类的对象就成为函数对象。如:
class CMyAverage {
public:
double operator(int a1, int a2, int a3) {
return (double)(a1 + a2 + a3);
}
};
CMyAverage average; // 函数对象
cout << average(3, 2, 3); // average.operator()(3,2,3)
示例程序:
#include<vector>
#include<iostream>
#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 {
private:
int power;
public:
SumPowers(int p) :power(p) {}
// 重载圆括号()
const T operator()(const T& total, const T& value) {
T v = value;
// 计算value的power次方
for (int i = 0; i < power - 1; ++i) {
v = v * value;
}
return total + v;
}
};
int main() {
const int SIZE = 10;
int a1[] = { 1,2,3,4,5,6,7,8,9,10 };
vector<int> v(a1, a1 + SIZE);
// 1 2 3 4 5 6 7 8 9 10
PrintInterval(v.begin(), v.end());
// v中的所有元素的平方和:385
int a = accumulate(v.begin(), v.end(), 0, sumSquares);
cout << a << endl;
// v中的所有元素的立方和:3025
int b = accumulate(v.begin(), v.end(), 0, SumPowers<int>(3));
cout << b << endl;
// v中的所有元素的四次方和:25333
int c = accumulate(v.begin(), v.end(), 0, SumPowers<int>(4));
cout << c << endl;
return 0;
}
我们以第一条调用accumulate
函数为例说明函数对象的作用,其他两条语句类似。
accumulate(v.begin(), v.end(), 0, sumSquares)
实例化出:
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;
}
上述的op
操作即为我们传入的参数sumSquares
,然后在循环中完成相关操作。此外,
S
T
L
{\rm STL}
STL还给我们提供了专门用于生成函数对象的函数对象类模板,我们可以使用这些模板来生成函数对象,如equal_to
、greater
、less
等等,均包含在头文件#include<functional>
中。如:
template<class T> struct greate:public binary_function<T, T, bool> {
bool operator()(const T& x, const T& y) const {
return x > y;
}
};
对于greater
的应用,在list
中存在两个排序函数sort
:
void sort();
将list
中的元素按照小于号规定的比较方法升序排列;template<class Compare> void sort(Compare op);
将list
中的元素按照指定的比较方法升序排列。
示例程序:
#include<list>
#include<iostream>
using namespace std;
class A {
public:
bool operator()(const int& c1, const int& c2){
// 两个数中个位较大者更大
return (c1 % 10) < (c2 % 10);
}
};
template<class T> void Print(T first, T last) {
// 输出[first,last)区间内的元素
for (; first != last; ++first) {
cout << *first << " ";
}
}
int main() {
const int SIZE = 5;
int a[SIZE] = { 5,21,14,2,3 };
list<int> lst(a, a + SIZE);
// 排序函数使用函数对象作为参数,即自定义的比较方式
lst.sort(A());
// 21 2 3 14 5
Print(lst.begin(), lst.end());
// 排序函数使用函数对象作为参数,使用默认排序规则
lst.sort(greater<int>());
// 21 14 5 3 2
Print(lst.begin(), lst.end());
return 0;
}
总结
S T L {\rm STL} STL是 C + + {\rm C++} C++中的重要组成,本文介绍其基本用法,比使用几个示例辅以解析。总的来说, S T L {\rm STL} STL为我们提供了一套快速实现功能的机制,如果想要更多地了解其机制及实现策略,可以阅读其源码。
参考
- 北京大学公开课:程序设计与算法(三)C++面向对象程序设计.