C++ Primer 第十一章 11.2 关联容器容器概述 练习和总结

11.2 关联容器概述

关联容器支持容器的通用操作,比如size_type这种类型,insert,erase,clear操作,以及值初始化等等。

这些都在9.2节里面。

11.2.1 定义关联容器

定义map需要指定关键字和值的类型。
定义set,需要指定关键字的类型。

在进行初始化时,
1.可以使用同类型的容器进行拷贝
2.也可以使用迭代器范围,只要迭代器内的元素可以转化为map的值的类型或者set的关键字的类型
3.可以使用值初始化。

map的值初始化,需要同时写上key和value。

map<string,string> authors = {
{"a","A"},
{"b","B"},
{"c","c"}
};

对于mutimap和mutiset的初始化也是一样的,但是mutimap,和mutiset可以让一个key对应着多个value。

为了方便理解,就认为set的key和value是一个值。

练习

11.5

map是key-value对的集合,set是key的结合。如果我们需要通过一个关键字查找某一个值,则使用map。我们只是简单的查找集合中,元素是否存在,则使用set。

11.6

set是key的集合,是关联容器,list可以顺序容器。
set按照关键字来查找元素,而list通过元素在容器中的位置来访问元素。

如果我们需要快速查找某个元素是否存在,可以使用set,如果需要在容器中频繁的插入和删除,使用list。

11.7
	map<string, vector<string>> family= {
		{"A",{"hello","world"}},
		{"B",{"dd","bbbb","cccc"}}
	};
	vector<string> new_item = {"cc","ccc"};
	family["C"] = new_item;
	family["A"].push_back("aaa");
	for (const auto& item:family) {
		cout << item.first<<":"<< std::ends;
		for (const auto& member:item.second) {
			cout << member << std::ends;
		}
		cout << endl;
	}
11.8
vector<string> vec;
	string word;
	while (cin>>word) {
		if (std::find(vec.begin(),vec.end(),word)==vec.end()) {
			vec.push_back(word);
		}
	}
	for (const auto&item:vec) {
		cout << item << endl;
	}

set可以直接添加元素,而不需要判断容器中是否已经存在该元素。

11.2.2 关键字类型的要求

在学算法时,学习到有的时候,需要迭代器指向的元素支持某一种操作符,如果该类型不支持这种操作符,则需要我们自己写函数来定义。

在关联容器中也是一样的**,我们可以自定义关键字的比较操作。默认情况关键字比较是使用类型的<运算符**

小于运算符的定义要遵循严格弱序,即:
在这里插入图片描述

使用关键字类型的比较函数

如果我们要使用自定义的操作来比较关键字,我们需要在创建容器的时候,指定比较函数的类型,并在创建的容器中用函数类型指针的对象初始化

听起来比较绕,其实就是

using f = bool(*)(const string&,const string&);

bool fun(const string&,const string&){
	
};
//f是类型的指针,func是类型的对象,它会自动的转为类型的对象的指针
set<string,f> my_set(func);

注意decltype(func)得到的是函数的类型,不是函数指针。

练习

11.9

我的理解是list保存关键字在文本中出现过的行号。

std::ifstream f_input("data.txt");
	string row_word;
	map<string, list<int>> word_count;
	int cur_row = 1;
	while (std::getline(f_input, row_word)) {
		std::istringstream s_input(row_word);
		string word;
		while (s_input>>word) {
			word_count[word].push_back(cur_row);

		}
		++cur_row;
	}

	for (const auto& item:word_count) {
		cout << item.first << " " << std::ends;
		for (const auto& item_1:item.second) {
			cout << item_1 << std::ends;
		}
		cout << endl;
	}

------------
data.txt文件
12 321 321 321 123 123 321 123
125454 345 654 47 654
654 7654 567 123 4321 3241
1234 213 123 4321 1234
1324 1234 123 4321 3421
32141 4321  341 32 11 123
11.10

考察迭代器是否会失效,以及容器的迭代器是否有<运算符。

不可以,虽然ivector<int>::terator有<运算符,因为vector的迭代器在增加或删除元素的过程中,可能会失效。如果创建了新的内存空间,意味着原来的迭代器全失效了。那么同样的begin(),就访问不了之前关联的元素了。

list<int>::iterator,也不行因为list<int>::iterator没有<运算,因为删除元素意味着,一部分的迭代器不见了,用这一部分的迭代器做key的int,也就访问不到了。

11.11

为了方便测试,我改了一下,为string类型定义了一个新的比较操作,认为大小一样就是相等。

这里使用类型别名来代替之前的dceltype()

在创建自定义比较操作的容器时,传入的<>是比较操作的类型,而传给对象的是,比较操作类型的一个对象。

bool compare_str_len(const string& str1,const string& str2) {
	return str1.size() < str2.size();
}

using com_str_len_f = bool(*)(const string& str1, const string& str2);


set<string, com_str_len_f> my_set(compare_str_len);
	string word;
	while (cin>>word) {
		my_set.insert(word);
	}
	for (const auto&item:my_set) {
		cout << item << endl;
	}

11.2.3 pair类型

pair类型,定义在utility头文件中,其保存两个数据成员。

创建pair时,需要指定两个数据成员的类型,数据成员类型不要求一致。看起来pair就是一个模板类

pair的默认构造函数对数据成员执行值初始化,pair中两个数据成员是public的。可以用first和second来访问。

在pair上可进行的操作为
在这里插入图片描述
pair可以使用列表初始化,传入两个参数进行初始化,以及使用make_pair创建pair。

对于pair类型的比较,首先比较first成员的大小,如果first成员相等,再比较second。

注意,我们创建对象时,可以不写对象的名字,pair<string,int>,string(“123”),这一点我在之前的编码中没有太在意

练习

11.12

下面的代码中,包含了三种创建pair的方法。

string str;
	int value;
	vector<std::pair<string, int>> vec;
	while (cin>>str>>value) {
		vec.push_back({str,value});
		vec.push_back(std::make_pair(str,value));
		vec.push_back(std::pair<string, int>(str, value));
	}
	for (const auto& item:vec) {
		cout << item.first << " :" << item.second << endl;
	}
11.13

在上一题的程序中分别使用了列表初始化、make_pair以及显式的初始化的方法来创建pair。

个人觉得make_pair()的方法最易于编写和理解,因为列表初始化可读性相对较低,而显式的创建编写比较繁琐,而make_pair()从名字就可以看出是创建一个pair,而且相对来说易于编写

11.14

下面的代码也使用了三种方法创建pair。
发现使用列表初始化一个结构较为复杂的容器时,很方便

map<string, vector<std::pair<string,string>>> family= {
		{"A",{{"a1","aa1"},{"a2","aa2"}}},
		{"B",{{"b1","bb1"},{"b2","bb2"},{"b3","bb3"}}}
	};
	vector<std::pair<string, string>> new_item = { {"c1","cc1"},std::pair<string,string>("c2","cc2") };
	family["C"] = new_item;
	family["A"].push_back(std::make_pair("a3","aa3"));
	for (const auto& item:family) {
		cout << item.first<<":"<< std::ends;
		for (const auto& member:item.second) {
			cout << member.first<<":"<<member.second << std::ends;
		}
		cout << endl;
	}
发布了54 篇原创文章 · 获赞 6 · 访问量 3312

猜你喜欢

转载自blog.csdn.net/zengqi12138/article/details/104361622