C++ Essential 个人总结(三)
第三章 泛型编程风格
Standard Template Library(STL)主要由两种组件构成:一是容器(container),包括vector、list、set、map等类,另一种组件时用以操作这些容器的所谓泛型算法(generic algorithm),包括find(),sort(),replace(),merge()等。
容器分为顺序性容器(sequential container)—— 按照顺序依次进行操作,多用到迭代(iterate)和关联性容器(associative container)—— 可快速查找容器中的元素值。
map类型为一对对的 key/value 组合,key用于查找,value用来表示要存储或取出的数据。set类型仅含key,用于判断某值是否存在其中。
泛型算法:与操作的元素类型无关的算法,主要通过 function template 技术,达到与操作对象类型相互独立的目的,其诀窍是通过一对iterator(first和last),标示着要进行迭代的元素范围。
3.1 指针的算术运算
可处理任何定义了equality(相等)运算分类型的find函数
代码
template <typename elemType>
elemType* find(const vector<elemType> &vec, const elemType &value)
{
for(int ix=0;ix<vec.size();ix++)
if(vec[ix] == value)
return &vec[ix];
return 0;
}
template <typename IteratorType, typename elemType>
IteratorType find(IteratorType first, IteratorType last, const elemType &value)
{
for(;first !=last;++first)
if(*first == value)
return first;
return last;
}
int main()
{
const int asize =8;
int ia[asize]={1,1,2,3,5,8,13,21};
//以ia的8个元素作为list和vector的初值
vector<int> ivec(ia,ia+asize);
list<int> ilist(ia,ia+asize);
int *pia = find(ia,ia+asize,1024);
if(ia!=ia+asize) //找到了
vector<int>::iterator it;
it=find(ivec.begin(),ivec.end(),,1024);
if(it!=ivec.end())
list<int>::iterator iter;
iter=find(ilist.begin(),ilist.end(),,1024);
if(iter!=ilist.end())
}
重新实现的find()函数使用了底部元素所属类型的equality(相等)运算符,如果底部元素未定义该运算符,则会出错,解决办法:
- 传入函数指针,取代原本的相等运算符
- 使用function object(特殊的class),在第四章有相关实现
泛型算法列表 - 搜索算法:
- 排序(sorting)及次序(ordering)整理算法:
- 复制、删除、替换算法:
- 关系算法:
- 生成与质变算法:
- 数值算法:
- 集合算法:
3.3 所有容器共同操作
容器类的(以及string类)的共通操作:
- equality(==)和inequality(!=)运算符,返回true或false
- assignment(=),将某个容器复制给另一个容器
- empty()会在容无任何元素时返回true,否则返回false
- size() 返回容器目前持有的元素个数
- clear() 删除所有元素
每个容器都提供begin()、end()、insert()、erase()操作:
3.4 使用顺序性容器
三种顺序性容器:vector、list、deque
list是以双向链接来存储内容,每个元素包含value,back指针(指向前一个),front指针(指向下一个),在list任意位置插入
deque是
使用顺序性容器方法
包含相应的头文件
#include <vector>
#include <list>
#include <deque>
//产生顺序性容器的方法
list<string> slist; // 产生空的容器
vector<int> ivec(32); //产生特定大小容器,每个元素以其默认值为初值
list<string> slist(16,"unassigned");
int ia[8]={1,1,2,3,5,8,13,21};
vector<int> fib(ia,ia+8); //通过一对iterator产生容器,iterator标示初值元素的范围
list<string> slist; //空容器
//填充slist
list<string> slist2(slist); //根据某个容器产生新容器,复制容器内的元素,作为新容器初值
PS:push_back()和pop_back()允许在容器末尾进行插入和删除操作。除此之外,list和deque(不包括vector)还提供了push_front()和pop_front(),pop_back()、pop_front()不会返回被删除的元素值,如果要读取最前端/后端元素,使用front()/back()
其他变形操作:
- iterator insert(iterator position,elemType value);
- void insert(iterator position, int count, elemType value);
- void insert(iterator1 position, iterator2 first, iterator2 last);
- iterator insert(iterator position)
3.5 使用泛型算法
使用泛型算法,首先需要包含头文件
#include <algorithm>
常用的算法可参考附录B
3.6 如何设计一个泛型算法
function object: 某种class实例对象,这类class对function call运算符做了重载操作,于是可以使function object当作一般函数使用。
标准库有定义一部分function object,分为算术运算,关系运算和逻辑运算三大类,使用需要包含头文件
#include <functional>
function object adapter:为了调整function object使得更好地应用于函数,包含:
binder adapter(绑定适配器):将function object地参数绑定至特定值,使得二元运算符变为一元运算符,标准库提供了bind1st(function object, value)、bind2nd(function object, value)
negator:对function object的真伪值取反,not1可对一元的function object取反,not2可对二元的取反。
3.7 使用map
map是key/value对,字典是map的一种实例。使用map,需包含头文件
map使用例子
#include <map>
#include <string>
map<string,int> words; //定义一个map对象
words["vermeer"]=1; //输入一个key/value对
//字数统计工作
string tword;
while(cin>>tword)
words[tword]++: //会取出相应的value,如果没有就创建,获得默认值0,再加一
map<string,int>::iterator it=words.begin();
for(;it!=words.end();++it)
cout<<"key:"<<it->first //map对象有两个特殊的member:first和second,分别对应key和value
<<"value:"<<it->second <<endl;
**查询map中是否存在某个key:
- 将key当作索引使用,但是如果找的key不存在,key会被自动加入该map中,value被赋值所属类型对应默认值;
- 利用map的find()函数,将key传入find()并调用
//查找为words.find("vermeer")
int count=0;
map<string,int>::iterator it;
it=words.find("vermeer");
if(it!=words.end())
count=it->second;
key在其中,则返回指向key/value对的iterator,否则返回end();
- 利用map的count()函数,count()会返回某特定项在map中的个数
3.8 使用set
Set由一群key组合而成,查询某值是否在集合内,用set
#include <set>
#include <string>
set<string> word_exclusion; //定义一个排除字眼的set
对任何key值,set只存储一份(存储多份相同的key值,需要multiset),set元素根据所属类型默认的从小到大顺序排列。
3.9 如何使用Iterator Inserter
为了解决目的端容器大小不确定的问题,标准库提供了三个 insert adaption,来避免使用容器的assignment运算符
- back_inserter(),使用back_push函数取代assignment运算符,其参数为目的端容器本身
vector<int> result_vec;
unique_copy(ivec.begin(),ivec.end(),back_inserter(result_vec));
- inserter(),使用insert()函数取代assignment运算符,其参数为容器和iterator(
- front—inserter以容器的push_front()函数取代assignment运算符,只适用于list和deque
这些adapter不能用才array上,因array不支持元素的插入操作
3.10 如何使用iostream Iterator
标准库定义有供输入输出使用的iostream iterator类,成为istream_iterator、ostream_iterator,分别支持单一类型的元素的读取和写入,需包含头文件
#include <iterator>
利用istream_iterator从标准输入设备读取字符串,需要设定一对iterator:
istream_iterator<string> is(cin); //将is绑顶至标准输入设备,作为first iterator
istream_iterator<string> eof; //在定义istream_iterator时不为其指定对象,便代表了end-of-file
ostream_iterator 标示字符串元素到标准输出
ostream_iterator<string> os(cout," ")
第二个参数可以是C-style字符串,也可以是字符串常量,表示输出时各个元素的分隔符。
当希望从文件中读取和写入时,将istream_iterator绑定到ifstream对象,将ostream_iterator绑定到ofstream对象
ifstream in_file("input_file.txt");
ofstream out_file("output_file.txt");
istream_iterator<string> is(in_file);
ostream_iterator<string> os(out_file," ");
浙公网安备 33010602011771号