0 迭代器
注意点:
- 迭代器的操作是有限的:均可自增、自减、==、!=、解引用、读取对象成员(.);
- 对于顺序容器vector支持迭代器加减一个整数,其他的常用容器不支持;
测试程序:
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <queue>
#include <map>
#include <set>
using namespace std;
int main(int argc,char *argv[])
{
vector<string> svec;
list<string> slist;
deque<string> sdeque;
map<string, int> smap;
set<string> sset;
//vector在内存是连续存储的,类似数组,可以通过加上一个整数来移动下标
vector<string>::iterator svec_it = svec.begin() + svec.size()/2;
//list<string>::iterator slist_it = svec.begin() + svec.size()/2; //error:list不支持加减一个整数
//deque<string>::iterator sdeque_it = svec.begin() + svec.size()/2; //error:deque不支持加减一个整数
//map<string, int>::iterator smap_it = svec.begin() + svec.size()/2; //error:map不支持加减一个整数
//set<string>::iterator sset_it = svec.begin() + svec.size()/2; //error:set不支持加减一个整数
return 0;
}
1 顺序容器
注意点:
- 指定元素个数的容器初始化只适用于顺序容器。如形式为C c(n,t)或C c(n)这两种形式的初始化。
- 直接将一个容器复制给另一个容器时,类型必须匹配:容器类型和元素类型都必须相同,如vector ivec2(ivec1);。
- 使用迭代器时,不要求容器类型相同,容器内的元素类型也可以不相同,只要相互兼容,能够将要复制的元素转换为所构建的形容器的元素类型,即可实现复制,如list slist(svec.begin(),svec.end());。
- 指针就是迭代器,允许通过使用内置数组中的一对指针初始化容器:
char *color[] = {“black”,”red”,”yellow”,”blue”};
size_t color_size = sizeof(color)/sizeof(char *);
list color1(color,color + color_size); - 除了引用类型,所有内置或复合类型都可以做元素类型。
- 除IO标准库类型之外,所有其他标准库类型都是有效的容器元素类型,容器本身也是。
- 容器的容器注意尖括号: vector< vector > lines;隔开,不然会导致编译时错误。
- list容器的迭代器既不支持算术运算(加法或减法),也不支持关系运算(<=,<,>=,>),只提供前置和后置的自增和自减运算以及相等和不等运算。
- 使用无效的迭代器会导致严重的运行时错误,如erase函数删除了元素,指向该元素的迭代器就无效了。
- 所有的顺序容器都支持push_back操作,在容器尾部插入一个元素的功能。而push_font只适用于list和deque容器类型。
- 不要存储end操作返回的迭代器,添加或删除deque或vector容器内的元素都会导致存储的迭代器失效。while(fisrt != v.end()){…}才好。
- 所有的容器都提供四种与容器大小相关的操作:size(),max_size(),empty(),resize(n),resize(n,t),包括关联容器。
- c.back()、c.front()分别返回容器c的最后一个元素的引用,如果c为空,则该操作未定义。相当于对begin()、end()-1进行解引用。
- pop_front()和pop_back()函数用于删除容器内的第一个和最后一个元素,当vector容器不支持pop_front操作,删除指定元素返回void。当然还有c.erase(p),c.erase(b,e)函数。
vector:
最常用的容器类型,支持对元素的快速随机访问,可以高效的在容器尾部添加和删除元素,但是在其他任何位置上的插入或删除运算则需要付出昂贵的代价。
list:
只支持元素的顺序访问,但是在list内部任何位置的插入和删除都非常迅速。
deque:
支持在首尾部快速的删除和插入元素
string:
字符串类型,一般的,可视为字符容器,支持大多数容器操作,与vector有大多数相同操作,但不支持栈方式操纵容器,即不能在string类型中使用font、back和pop_back操作。
s.substr(pos,n),返回一个下标从pos开始的n个字符的string。
s.substr(pos),返回一个下标从pos开始到s末尾的所有字符的string。
s.replace(pos,len,args),删除s中从下标pos开始的len个字符,用args指定的字符替换,返回s的引用。
s.replace(b,e,args),删除迭代器b和e标记范围内的所有字符,用args代替,返回s的引用。
s.find(args),在s中查找args第一次出现的pos,没有则返回string::npos
s.rfind(args),在s中查找args最后一次出现的pos
s.find_first_of(args)
s.find_first_of(string),查找任意字符,s中是否有string中的字符。
s.find_first_of(string,pos);
s.find_last_of(args)
s.find_first_not_of(args)
s.find_last_not_of(args)
2 关联容器
注意点:
- 关联容器不提供front、push_front、pop_front、back、push_back及pop_back操作。
- map是pair类型(对组类型)的集合,键+值
- 在使用迭代器遍历map容器时,迭代器指向的元素按键的升序排列。
- map的键值不能修改,为const修饰,只能修改值成员。
- 对map迭代器进行解引用将产生pair类型的对象,即可以使用first或second获取内容。
- 使用下标访问map与使用下标访问数组或vector的行为截然不同:用下标访问不存在的元素将导致在map容器中添加一个新的元素,它的键即为该下标。使用insert代替下标运算,可以避免这个不必要的初始化。使用insert插入元素时,如果键已经存在容器中,则insert不做任何操作,即不做值替换。
- set不支持下标操作符,键值唯一,不能修改。
- multimap中,同一个键可以关联多个值,且同一个键所关联的元素必然相邻存放。因此只要找到首端迭代器,就可以遍历同一键所关联的所有的值。
- 使用关联容器需包含对应的头文件,如
#include<map>
、#include<set>
map
m.count(k),返回m中k出现的次数,适用于multimap和map,很明显,在map中,如果k存在,则返回值肯定为1。
m.find(k),在m中按k索引,存在,则返回指向该元素的迭代器。不存在,则返回超出末端迭代器。
m.erase(k),
m.erase(p),
m.erase(b,e),
m.insert(e),e是一个用在m上的value_type类型的值。
m.insert(b,e)
m.insert(iter,e)
set
insert
count
find
erase
multimap
multiset
3 应用:文本查询
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <set>
#include <cstdlib>
#include <stdexcept>
#include <sstream>
using namespace std;
class TextQuery{
public:
typedef vector<string>::size_type line_no;
void read_file(ifstream &is);
set<line_no> run_query(const string&) const;
string text_line(line_no) const;
private:
ifstream& open_file(ifstream &,const string &);
void store_file(ifstream&);
void build_map();
vector<string> lines_of_text;
map< string, set<line_no> > word_map;
};
void TextQuery::read_file(ifstream &is)
{
store_file(is);
build_map();
}
string TextQuery::text_line(line_no line) const
{
if(line < lines_of_text.size())
return lines_of_text[line];
//out_of_range,c++异常类,
throw out_of_range("line number out of range");
}
set<TextQuery::line_no> TextQuery::run_query(const string &query_word) const
{
map<string, set<line_no> >::const_iterator loc = word_map.find(query_word);
if(loc == word_map.end())
//返回空set对象
return set<line_no>();
else
return loc->second;
}
ifstream& TextQuery::open_file(ifstream &in,const string &file)
{
in.close();
in.clear();
in.open(file.c_str());
return in;
}
//-----------------------------------------------------------
//将文件中的每一行字符串作为一个元素依次存放到vector中
//-----------------------------------------------------------
void TextQuery::store_file(ifstream&is)
{
string textline;
while(getline(is,textline))
lines_of_text.push_back(textline);
}
//-----------------------------------------------------------
//将存放每一行字符串的vector的元素依次取出,将其分解为单词,并将单词的行数保存到map< string, set<line_no> >中
//-----------------------------------------------------------
void TextQuery::build_map()
{
for(line_no line_num = 0; line_num != lines_of_text.size(); ++line_num){
//绑定行字符串到istringstream
istringstream line(lines_of_text[line_num]);
string word;
//循环从行字符串读取单词到string类型word
while(line >> word){
//将行号插入到键值为word,值为vector类型的map中
word_map[word].insert(line_num);
}
}
}
string make_plural(size_t ctr, const string &word,
const string &ending)
{
return (ctr == 1) ? word : word + ending;
}
void print_results(const set<TextQuery::line_no>& locs,
const string& sought,const TextQuery &file)
{
typedef set<TextQuery::line_no> line_nums;
line_nums::size_type size = locs.size();
cout << "\n" << sought << " occurs "
<< size << " "
<< make_plural(size,"time","s") << endl;
line_nums::const_iterator it = locs.begin();
for(; it != locs.end(); ++it){
cout << "\t(line "
<< (*it) + 1
<< ") "
<< file.text_line(*it) <<endl;
}
}
int main(int argc,char *argv[])
{
ifstream infile;
if(argc < 2 || !open_file(infile,argv[1])){
cerr << "No input file!" << endl;
return EXIT_FAILURE;
}
TextQuery tq;
tq.read_file(infile);
while(true){
cout << "Enter word to look for,or q to quit: ";
string s;
cin >> s;
if(!cin || s == "q")break;
set<TextQuery::line_no> locs = tq.run_query(s);
print_results(locs,s,tq);
}
return EXIT_SUCCESS;
}