在读C++Primer,这几天看到关联容器了,一下子感觉容器乃神器啊,可是内容也有点小多,所以在此梳理一下,以备后续查询方便
一、pair类型
pair类型是与关联容器密切相关的一种简单的标准库类型,该类型在utility头文件中定义。
1.pair的创建和初始化:
pair是一种模板类型,创建时必须提供两个类型名:pair对象所包含的两个数据成员各自对应的类型名字。
例如:
pair<string, string> anon//存储两个string
pair<string, int> word_count;//存储一个string, 一个int
pair<string, vector<int> > line;//存储一个string, 一个vector类型。
以上创建时都未初始化,所以会自动调用默认构造函数初始化值,如上面的string会被初始化为空,int 被初始化为0,vector也为空。
下面是初始化:
pair<string, string> author("James", "Joyce");
pair类型使用相当麻烦,为此可用typedef简化其声明:
typedef pair<string, string> Author;
Author proust("Marcle", "Proust");
Author joyce("James", "Joyce");
如上,简化后使用方便了许多。
2.pair对象的操作:
pair类可以直接访问其成员,即,其成员是公有的,分别为first 和second,用点操作符-----成员访问标志即可访问其成员:
string firstbook;
if(author.first == "James" && author.second == "Joyce")
firstbook = "Stephen Hero";
3.生成新的pair对象:
除了使用构造函数,还可以用make_pair函数,由传递给它的两个实参生成一个新的pair对象:
例如:
pair<string, string> next_auth;
string first, second;
while(cin >> first >> second)
next_auth = make_pair(first, second);
二、关联容器:
(一).map:
Map是一个键—值对的集合,就像一个关联数组:键相当于数组的下标,而值相当于下标所对应存储的值,只不过map的键作为下标时不只是int, 可以使任何其他类型,比如:string。
1.定义:
使用map必须包含map头文件!分别指明键和值得类型即可:
map<string, int> word_count;
上面的声明表明,这个map类型是以string类型作为键值,即意义上的下标,int作为值存储。
注意:键类型唯一的约束就是,必须支持< 操作符,比如我们上面定义的word_count,键为string,满足<操作符,所以可用作map类型的键
2.map定义的类型:
map每个元素分为键以及键关联的值,map的value_type就反映了这个事实,value_type是存储元素的键以及值得pair类型,而且键为const。
例如,我们上面定义的word_count的value_type是pair<const string, int>类型。
(a).map迭代器进行解引用将产生pair类型的对象:
对迭代器解引用,将得到一个value_type类型的值。对于map,它是pair类型。
例如:
map<string, int>::iterator map_it = word_count.begin();
cout << map_it->first;
cout << " " << map_it->second;
map_it->first = "new key";
++map_it->second;
解引用得到一个pair对象,first成员存放键,为const,second成员存放值
3.使用下标访问map对象:
例如:
map<string, int> word_count;
word_count["Anna"] = 1;
这个是典型的用下标,即键值访问对象的值。如果该键已经在map中,则下标运算返回该键所关联的值;当该键不存在时,map会为该键创建一个新的元素,并将它插入map中。
注意:map迭代器返回value_type类型的值,包含const key_type和mapped——type类型成员的pair对象。而下标操作符则返回一个mapped_type类型的值
注:下标访问的副作用:当map中不包含该键时,会自动添加新元素!!!
我们可以利用这一副作用,例如下面的单词计数程序:
map<string, int> word_count;
string word;
while(cin >> word)
++word_count[word];
这使得我们的程序异常简洁!
4.map::insert的使用:
(a).以insert代替下标运算:
直接使用insert成员函数,其语法更为紧凑:
word_count.insert(map<string, int>::value_type("Anna", 1));
它的实参是一个pair对象,因为value_type类型就是pair类型的同义词,insert的实参创建了一个pair对象,将该对象插入map容器。
注:使用insert成员函数可以有效避免下标操作的副作用:不必要的初始化。
另外,上面的实参略显臃肿,可以用下面的方法简化:
(1).使用make_pair
word_count.insert(make_pair("Anna", 1));
(2).使用typedef
word_count.insert(valType("Anna", 1));
这两种方法都大大简化了代码,提高了程序的可读性。
(b).检测insert的返回值:
由于map中一个键只对应一个元素,如果试图插入的元素已经在容器中,则insert函数将不做任何操作。因此,含有一个或一对迭代器的形参的Insert函数不能说明有多少元素插入容器其中!
但是,带有一个pair形参的insert版本将返回一个值:包含一个迭代器和一个bool值得pair对象,其中,迭代器指向map中具有相应键的元素,而bool值表示是否插入该元素。
如果该键已在容器中,则关联的值不变,返回的bool值为false;如果不在容器中,则插入新元素,bool值变为true。这两种情况下,迭代器都将指向具有给定键的元素。
例如:下面是一个用insert重写的单词计数程序:
map<string, int> word_count;//定义一个map变量
string word;
while(cin >> word)
{
//ret是一个人pair变量,insert函数返回一个pari变量,将返回值赋给ret...
//pair的第一个成员是一个map类型的迭代器,保存insert返回的指向插入元素的迭代器,
pair<map<string, int>::iterator, bool> ret = word_count.insert(make_pair(word, 1));
if(!ret.second)
++ret.first->second;
}
5.查找并读取map中的元素:
因为用下标查找时,容器会自动插入一个不存在该键值的元素,所以不适合查找。
(a).使用count检查某键是否存在
对于map而言,count的返回值不是1就是0,因为map只允许一个键对应一个值,所以count可以有效的表明一个键是否存在!如果返回值非0,则这时可放心的用下标访问该元素而不必担心会插入新元素。
int occurs = 0;
if(word_count.count("foobar"))
occurs = word_count["foobar"];
然而,
执行完count后再用下标,实际上是对元素进行了两次查找,若希望当元素存在时就使用它,应该用下面的find操作。
find操作返回指向元素的迭代器,若元素不存在,则返回end迭代器。
如果希望当具有某键的元素存在时就获取该元素的引用,否则就不在容器中创建该元素,那么应该使用find。
int occurs = 0;
map<string, int>::iterator it = word_count.find("foobar");
if(it != word_count.end())
occurs = it->second;
6.从map中删除元素
m.erase(k) //删除m中键为k的元素。返回size_type类型的值,表示删除的元素个数
m.erase(p) //删除m中迭代器p指向的元素,p必须指向m中确实存在的元素,而且不能等于m.end()。返回void类型
m.erase(b, e) //删除迭代器区间里的值,返回void类型