首页 > 编程语言 > 详细

C++ Essential 个人总结(三)

时间:2021-02-15 23:20:30      阅读:26      评论:0      收藏:0      [点我收藏+]

第三章 泛型编程风格

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;
	}

数组传递给函数,或是由函数返回,仅有第一个元素的地址被传递。
举例:int min(int *array){...}min 函数可接受任意大小的数组,但不知道array大小,针对此问题,可有以下两种解决办法

  • 解法一:增加一个参数,表示array的大小
    template <typename elemType> elemType* find(const elemType *array,int size,const elemType &value);
  • 解法二:传入另一个地址,指示array读取操作的终点(将此值称为“标兵”)
    template <typename elemType> elemType* find(const elemType *array, const elemType *sentinel,const elemType &value);
    传入的第二个地址标示出数组最后一个元素的的下一个地址,不可以对该地址进行读取或写入操作,仅仅拿该地址和其它地址进行比较就没有问题。
    在对vecotr容器进行操作时,需要检测vector是否是空的
vector<string> svec; //存储string地vector
if(!svec.empty())   //当不是空的就可以调用find函数了  

容器list

list元素以一组指针相互链接(linked):前向(forward)指针指向下一个(next)元素,后向(backword)指针指向上一个(preceding)元素。
由于list地所有元素宾补储存在连续的内存空间里,所以底层指针的工作方式和vector、array不同,但通过进一步扩展和抽象,可以使得find()函数也支持list,即Iterator

3.2 泛型指针(Iterater)

设计一组iterator class,使得可以使用“和指针相同的语法”进行程序编写,需要设计iterator class的deference(提领,*)、inequity(不等,!=)、increment(递增,++),由类内的inline函数提供。其实现效果为:对list iterator而言,其递增函数会沿着list的指针前进到下一个元素,对vector iterator,递增函数是增加地址值来前进到下一个元素。

  • 如何取得iterator
    每个标准容器提供begin(),end()操作函数,分别提供指向第一个元素、最后一个元素的下一位置的iterator。
    iterator定义应提供的信息:
  • 1、迭代容器的类型(vector,list),这决定了访问下一个元素的方式
  • 2、iterator 所指的元素类型,决定iterator提领操作的返回值
    标准库提供的iterator语法:
vector<string> svec;
//iter 指向一个vector,vector元素类型为string
//iter初始值为第一个元素
vector<string>::iterator iter = svec.begin();

PS:双冒号(::)表示iterator是string vector定义内的嵌套类型。
对于const vector,使用const_iterator 进行遍历操作,const_iterator只允许读取vector的元素,但不允许任何写入操作

const vector<string> cs_vec;
vector<string>::const_iterator iter = cs_vec.begin();
cout<<*iter<<endl; //使用一般指针提领方式接可以取得iterator所指的元素值
cout<<iter->size()<<endl //使用arrow运算符可以调用底部string元素所提供的操作

对find函数重新实现,使得同时支持两种形式:一对指针或是一对指向某容器的iterator

find函数重新实现
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," ");

C++ Essential 个人总结(三)

原文:https://www.cnblogs.com/senyablog/p/14401372.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!