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之後出現異常,但是如果我再換一下位置,這個問題可能我當時就會發現。測試的時候我也儘量從使用者的角度去考慮,從使用者的角度會不會覺得某些地方有點彆扭,或者某些地方的設計並不是很合理,也就是說測試的階段在一定程度上起到了優化的作用。這就是我最近軟件開發的心得吧,前面的路還有很長,希望我保重身體,保持奮鬥的心,進無止境,精益求精,加油!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章