C++ Primer第十一章①

C++ Primer

关联容器

前面我们学的都是顺序容器,顺序容器中的元素是按它们在容器中的位置来保存和访问的。接下来这一章我们要学习关联容器:关联容器中的元素是按关键字来保存和访问的。
关联容器的很多行为与顺序容器相同,但那些不同之处反映了关键字的作用。

使用关联容器

大多数程序员都很熟悉vector这一套,但很多人可能从未用过关联容器,接下来让大家看看关联容器在某些场合是多么牛逼。

使用map

我们要统计每个单词在输入中出现的次数:

map<string, size_t> word_count;
string word;
while(cin >> word)
{
    ++word_count[word]; //你还能用顺序容器写出更简单的程序吗
}
上一个程序的一个合理拓展是:忽略常见单词,如the,and等,我们可以用set来保存想忽略的单词:
map<string, size_t> word_count;
set<string> exclude = {"the", "and", "but"};
while(cin >> word)
{
    if(exclude.find(word) == exclude.end())
    {
        ++word_count[word];
    }
}

关联容器概述

关联容器都支持一般的普通容器操作,但是不支持顺序容器的位置相关操作,例如push_front,因为关联容器中元素是根据关键字存储的。

定义关联容器
set<string> exclude = {"1", "2"};
map<string, string> authors = {
    {"a", "haha"},
    {"b", "hehe"}
}
multi差别
vector<int> ivec = {1, 1, 2, 2};
set<int> iset(ivec.cbegin(), ivec.cend());
multiset<int> miset(ivec.cbegin(), ivec.cend());
cout << iset.size() << endl; //2
cout << miset.size() << endl; //4

关键字类型的要求

关键字就是键,值就是值,它俩一起就是键值对,对于键的要求就是:键必须定义元素比较的方法,这个要求很好理解嘛。我们前面用到的键都是string或int之类的,元素比较的方法都已经帮我们写好了,所以我们可以很轻松地不用管,接下来就要介绍,如果键的类型是自定义的一些数据类型,那我们在定义比较方法的时候有哪些注意的呢?

必须定义一个严格弱序(strict weak ordering),什么意思呢,它必须具备如下性质:

  • 若k1<=k2,则k2不能<=k1 (这个跟我们数学学的不一样啊)
  • 若k1<=k2, 且k2<=k3,则k1<=k3,这个好理解,传递性
  • 若k1不小于等于k2, k2也不小于等于k1,则k1 == k2

接下来我们就来举个例子。定义Sales_data的multiset,显然我们不能直接这样写:

multiset<Sales_data> a; //错的,因为Sales_data没有<运算符

所以,我们要自定义一个比较操作,这个函数其实我们之前写过:

bool compareIsbn(const Sales_data &a, const Sales_data &b)
{
    return a.isbn() < b.isbn(); //这里能用<,还是因为isbn是string,string有<
}

好了,现在已经自定义好比较操作了,那我们怎么使用它,或者说怎么让编译器知道它呢,先抛出代码:

multiset<Sales_data, decltype(compareIsbn)*> bookstore(compareIsbn);

这个定义很长,里面还有很多老朋友,我们来仔细看看(下面这段很啰嗦,但值得你仔细看看):
要使用自定义的操作,我们在定义multiset时就要在键后面加上比较操作类型,这个比较操作类型是函数指针,指向我们定义的比较操作函数,这样我们就解释了前面的类型部分;后面bookstore加括号,意思是我们要调用compareIsbn来初始化bookstoe,这个怎么初始化呢?它和之前的初始化意思不太一样,它的意思是当我们添加元素时,用compareIsbn来为这些元素排序(就是说,它初始化不是提供元素,是提供元素排序的方式),这个小括号里的compareIsbn类型是函数指针,别忘了我们在使用函数名的时候,它会自动转化为指针,那么问题来了,为什么前面那个compareIsbn还要加 表示指针呢?因为decltype比较特殊啊,它得出的类型就是函数类型,所以要加 号表示指针。

pair类型

接下来介绍一个map的好基友,pair,我们会经常用到它,它定义在头文件utility中。 一个pair保存两个数据成员:

pair<string, string> a;
pair<string, size_t> b;
pair<string, vector<int>> c; //反正什么都能装,类似容器

pair的默认构造函数对数据成员进行值初始化,我们也可以提供初始化器来显式初始化:

pair<string, string> d{"张无忌", "赵敏"};

pair有个很特殊的规定它很大方,它的数据成员都是public的,而且两个成员分别命名为first和second,这样就方便所有人来访问,我家大门常打开。

cout << d.first << d.second << endl;

关于pair上的操作以后碰到了再解释,比较简单的。

创建返回值为pair对象的函数

pair<string, int> process(vector<string> &v)
{
    if(v.empty()){ return {v.back(), v.back().szie(); } //列表初始化,
    else {return pair<string, int>();} //调用默认构造函数构造临时对象返回
}
#C++工程师#
全部评论
棒!
点赞 回复 分享
发布于 2016-12-20 19:23

相关推荐

10-30 10:16
南京大学 Java
龚至诚:给南大✌️跪了
点赞 评论 收藏
分享
6 收藏 评论
分享
牛客网
牛客企业服务