C++STL专题

STL概述

STL由一些可适应不同需求的集合类(collection class),以及在这些数据集合上操作的算法(algorithm)构成。
容器(Container) - 管理某类对象的集合
容器分为序列式容器和关联式容器,所有容器中存放的都是而非引用。如果希望存放的不是副本,容器元素只能是指针。
在这里插入图片描述

迭代器(Iterator) - 在对象集合上进行遍历
算法(Algorithm) - 处理集合内的元素
STL内的所有组件都由模板(template)构成,其元素可以是任意类型。
一定不要忘记<>!!

STL的区间一般都是左闭右开:[first,last)。
在这里插入图片描述

常用操作

与大小相关的操作

size()-返回当前容器的元素数量
empty()-判断容器是否为空
max_size()-返回容器能容纳的最大元素数量
如果两个容器内的所有元素按序相等,那么这两个容器相等(==),一般不作比较。

赋值和交换

swap()

与迭代器(iterator)相关的操作

begin()-返回一个迭代器,指向第一个元素
end()-返回一个迭代器,指向最后一个元素之后
rbegin()-返回一个逆向迭代器,指向逆向遍历的第一个元素
rend()-返回一个逆向迭代器,指向逆向遍历的最后一个元素之后

对元素操作

insert(pos,e)-将元素e的拷贝安插于迭代器pos所指的位置
erase(beg,end)-移除[beg,end]区间内的所有元素
clear()-移除所有元素

常用容器

vector

vector模拟动态数组,可以实现动态的增删,也可以随机的存取

map/multimap

使用平衡二叉树管理元素,元素包含两部分(key,value),key和value可以是任意类型。map容器是键-值对的集合。
map中不允许key相同的元素,multimap允许key相同的元素。
根据元素的key自动对元素排序,因此根据元素的key进行定位很快(树形结构),因此可用作索引
map集合元素插入后自动排序

set/multiset

使用平衡二叉树管理元素,集合(Set)是一种包含已排序对象的关联容器。set容器只是单纯的键的集合。可用于判断某值是否存在。
set中不允许key相同的元素,multiset允许key相同的元素。
set集合元素插入后自动排序

pair

pair模板可以用于生成 key-value对

实际使用

利用map进行查找

可以使用vector动态数组来存储原始信息,当需要查找的时候如果直接查找的话要一个一个的从头开始遍历,效率较低,借助map的键值对的特点,我们可以利用map来建立索引。
实现方法:在建立vector的同时,也同时将该元素插入到map容器中,map的first是待查找的关键字,second是该元素在vector中的下标。

如果是在导入数据时建立map,可以按照下面这样写:

vector.push_back(element);
mapname.insert(make_pair(element.getSname(), vector.size() - 1));

如果是在处理过程中构造或者重构map,按照以下的方式写:

	map1.clear();
	map2.clear();
	vector<T>::iterator it, first = vector.begin();
	for (it = vector.begin(); it != vector.end(); it++)
	{
		map1.insert(make_pair(it->getSname(), distance(first, it)));
		map2.insert(make_pair(it->getSno(), distance(first, it)));
		//map1.insert(make_pair(it->getSname(), it - vector.begin()));
		//map2.insert(make_pair(it->getSno(), it - vector.begin()));
	}

在查找时,如果结果唯一:

void Operation::findBookById(string id)
{
	multimap<string, int>::iterator it;
	it = bookid.find(id);
	if (it == bookid.end())
		return;
	else
		cout << book[it->second];
}

如果结果不唯一:要用到lower_bound(满足条件的下界)和upper_bound(满足条件的下界)然后遍历lower_bound到upper_bound之间

void Operation::findBookByName(string name)
{
	multimap<string, int>::iterator it, p1, p2, p;
	it = bookname.find(name);
	if (it == bookname.end())
		return;
	else {
		p1 = bookname.lower_bound(name);
		p2 = bookname.upper_bound(name);
		for (p = p1; p != p2; p++) {
			cout << book[p->second] << endl;
		}
	}
}

要注意:一旦vector发生变化(如删除了元素或者修改了元素的关键字),对应的map一定要相应变化,否则会使得map与vector之间不对应,查找时候会产生错误
而一般如果删除了元素,map是进行重构;如果修改了关键字,map将此元素删除后再在map的后面insert一个新的元素即可,因为map自带自动排序功能。

void Operation::rebuildBookMap()
{
	bookname.clear();
	bookid.clear();
	vector<Book>::iterator it, first = book.begin();
	for (it = book.begin(); it != book.end(); it++)
	{
		bookname.insert(make_pair(it->getBname(), distance(first, it)));
		bookid.insert(make_pair(it->getIsbn(), distance(first, it)));
		//bookname.insert(make_pair(it->getBname(), it-book.begin()));
		//bookid.insert(make_pair(it->getIsbn(), it - book.begin()));
	}
}
void Operation::modifyBook(string id)
{
	multimap<string, int>::iterator it;
	vector<Book>::iterator first = book.begin();
	string pre;
	it = bookid.find(id);
	if (it == bookid.end())
		return;
	else
	{
		pre = book[it->second].getIsbn();
		cin >> book[it->second];
		int index = it->second;
		if (book[it->second].getIsbn() != pre)
		{

			bookid.erase(it);
			bookid.insert((make_pair(book[index].getIsbn(), index)));
		}
		cout << book[index];
	}

}

利用find_if函数结合map实现模糊查找

string内部的find函数本身可以实现对任意子串的查找,也就实现了模糊查找。问题在于必须给map的first准确的关键字,否则会查不出来,这就需要对find_if函数的谓词函数进行改造。

template<class InIt, class Pred> 
InIt find_if(InIt first, InIt last, Pred pr); 

返回区间 [first,last) 中的迭代器 i, 使得 pr(*i) == true

为此,我构造了一个类,并重载了函数调用运算符,在其内部实现取子串查找。(STL中常常这样构造类)

class map_value_finder
{
public:
	map_value_finder(const string& cmp_string) :m_s_cmp_string(cmp_string) {}
	bool operator ()(const multimap<string,int>::value_type& pair)
	{
		int loc;
		loc=pair.first.find(m_s_cmp_string);
		if (loc != pair.first.npos)
			return true;
		return false;
	}
private:
	const string& m_s_cmp_string;
};
void Operation::fuzzyFindBname(string str)
{
	multimap<string, int>::iterator it, p1, p2, p, begin;
	p1 = bookname.begin();
	p2 = bookname.end();
	begin = p1;

	for (p = p1; p != p2; p++)
	{
		it = find_if(begin, bookname.end(), map_value_finder(str));
		if (it == bookname.end())
			return;
		else
			cout << book[it->second] << endl;
		it++;
		begin = it;
	}

}

区间查找

可以利用lower_bound到upper_bound来实现上界和下界的值的确定,然后遍历其中的元素即可

void Operation::findBookByTime(string t1, string t2)
{
	multimap<string, int>::iterator it1, it2, p;
	it1 = booktime.lower_bound(t1);
	it2 = booktime.upper_bound(t2);

	for (p = it1; p != it2; p++)
	{
		cout << book[p->second] << endl;
	}
}

多条件组合查询

利用集合的交并集来实现,将所有的关键字全部查出来,取出其值(second)放入集合中,将多个条件产生的集合做交操作即可。
交操作的函数前四个参数分别代表待操作的集合的起始和结束点,最后一个参数表示将操作后的集合保存到哪里。

set_intersection(bpub.begin(), bpub.end(), bname.begin(), bname.end(), insert_iterator<vector<int>>(res, res.begin()));
void Operation::multifind(string str1,string str2)
{
	vector<int> res;
	vector<int>::iterator v;
	multiset<int> bpub;
	multiset<int> bname;
	multimap<string, int>::iterator it, p1, p2, p;
	it = bookpub.find(str1);
	if (it == bookpub.end())
		return;
	else {
		p1 = bookpub.lower_bound(str1);
		p2 = bookpub.upper_bound(str1);
		for (p = p1; p != p2; p++)
		{
			bpub.insert(p->second);
			
		}
	}

	it = bookname.find(str2);
	
	if (it == bookname.end())
		return;
	else {
		p1 = bookname.lower_bound(str2);
		p2 = bookname.upper_bound(str2);
		for (p = p1; p != p2; p++)
		{
			bname.insert(p->second);
		}
	}

	set_intersection(bpub.begin(), bpub.end(), bname.begin(), bname.end(), insert_iterator<vector<int>>(res, res.begin()));
	for (v = res.begin(); v != res.end(); v++)
	{
		cout << book[*v]<<" ";
	}
}

感想

首先反思一下最近的学习状态,最近的学习状态不是很好,白天楼下的邻居在装修,搞得在家里学习上课都心烦意乱的,写代码也是不太容易进入状态,所以一气之下搬到了楼下车库里,在车库学习虽然听不见噪音,但还是没有学习的环境,而且天天搬来搬去的,弄得白天很不在状态;只有晚上夜深了才能更好的静下心来学习,所以我最近熬夜熬到很晚,熬到两三点都是常见的事,但是随之而来的就是生物钟的紊乱,搞得该吃饭的时候不吃饭,该睡觉的时候不睡觉,很是难受。通过这个事情我知道了有些环境是改变不了的,能改变的只有自己。这学期可能是我大学四年课业压力最大的一年,学分已经修到爆,光主课就有9门,每一门都不简单,另外还有项目方面的事情需要操心,还正巧摊上了疫情,所以在是时间上是空前的紧张,今后的学习还是要进一步提高效率,在家学习虽然灵活度更高,但是一定要自己安排好时间,挺过最难的这一学期。

接下来谈谈最近的收获,最近收获挺大的,学到的知识应该要学会用,上课听是一码事,会用是另一码事。就比如现在学的这个STL,STL是别人已经写好的一些工具,简单的说我们会使用即可,也就是我们只要学习其参数的含义会调用就行,但是从更深层次来说,我们作为计算机专业的学生,需要深究这个黑箱内部的东西。一方面,只是看过一遍或者只是简单使用一遍,印象不够深刻,过上一段时间可能就忘得一干二净,这就需要我们去真正把学过的东西正在应用起来,比如可以利用STL来进行系统的开发,利用STL进行开发不仅在代码量上更加简洁,而且STL本身提供的算法一般来说效率都比较高,另一方面,我们还要了解STL内部实现的原理,知其然更要知其所以然嘛,更有利于我们后续的学习。比如假期里我学了matlab,仅仅是看过一遍,没怎么上手,这两天老师一问就慌了,从寒假到现在短短两个月的时间就差不多全忘了,所以我们学习知识一定要学会实际使用。也就是what、how、why中重点掌握how。在这个学习过程中一定会出现各种各样的问题,但是我感觉现在的代码的每一个字母都要自己敲上,每出现一个bug都要自己找错、改错,这样从一开始就积累自己的开发经验,虽然可能耗费的时间会多一些,但是打好了基础,我相信是一定可以厚积薄发的。

最后写一下我的开发经验,关于文件的系统开发流程在上一篇博客C++文件操作专题提过了,简单来说就是,从文件读入数据到内存->内存处理->将处理的数据写回文件。这其实和我们大一开始熟悉的编程思想是一样的,只不过把从键盘输入变成了从文件读入,把到显示屏输出换成到文件输出,基本上都是“三段式”的感觉。下面谈一下最近的开发感想,其实学到现在我感觉开发的过程并不是很难,我们所做的编程说白了就是把人的思想告诉计算机,描述的方法也就是我们所用的编程语言构成的算法,要想开发出好程序,一开始人的思想一定要清晰,最好自己脑子里先有一个框架,根据这个框架或者说是流程,把我们所想的用计算机语言描述出来,这也就是简单系统的开发,所以,人的思路是编程中最重要的,整体框架一定要实现规划好,具体实现的时候需要用到什么算法、什么知识点就考察我们的编程基础了,这个其实也需要我们有一个清晰的思路,把整个系统完整的实现出来。接下来就到了debug阶段,这个阶段应该是耗时间最长的,可能一个晚上就能把主体代码搞定,但是改错调试要好几天,这个阶段我刚刚说过,需要自己不断的摸索,是需要积累经验的,见得多了自然改的就快了。最后测试的时候也要注意各种情况尽量都要考虑到,比如上次的delete函数没有重构的问题,当时delete函数我写在了最后,所以自然不会在delete之后出现异常,但是如果我再换一下位置,这个问题可能我当时就会发现。测试的时候我也尽量从使用者的角度去考虑,从使用者的角度会不会觉得某些地方有点别扭,或者某些地方的设计并不是很合理,也就是说测试的阶段在一定程度上起到了优化的作用。这就是我最近软件开发的心得吧,前面的路还有很长,希望我保重身体,保持奋斗的心,进无止境,精益求精,加油!

猜你喜欢

转载自blog.csdn.net/qq_45654306/article/details/105803326