日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

《C++标准程序库》笔记之二

發(fā)布時間:2025/3/13 c/c++ 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《C++标准程序库》笔记之二 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

《C++標(biāo)準(zhǔn)程序庫》筆記之二

本篇博客筆記順序大體按照《C++標(biāo)準(zhǔn)程序庫(第1版)》各章節(jié)順序編排。

--------------------------------------------------------------------------------------------

6. STL 容器

6.1-1

本節(jié)講述STL容器的共通能力。其中大部分都是必要條件,所有STL容器都必須滿足那些條件。三個最最核心的能力是:

(1)所有容器提供的都是“value語意”而非“reference語意”。容器進(jìn)行元素的安插操作時,內(nèi)部實施的是拷貝操作,置于容器內(nèi)。因此STL容器的每一個元素都必須能夠被拷貝。如果你打算存放的對象不具有public copy構(gòu)造函數(shù),或者你要的不是副本(例如你要的是被多個容器共同容納的元素),那么容器元素就只能是指針(指向?qū)ο?#xff09;。參見上一篇博客C++標(biāo)準(zhǔn)程序庫筆記之一,5.10節(jié)。

(2)總體而言,所有元素形成一個次序(oder)。也就是說,你可以依相同次序一次或多次遍歷每個元素。每個容器都提供“可返回迭代器”的函數(shù),運用那些迭代器你就可以遍歷元素。這是STL算法賴以生存的關(guān)鍵接口。

(3)一般而言,各項操作并非絕對安全。調(diào)用者必須確保傳給操作函數(shù)的參數(shù)符合需求。違反這些需求(例如使用非法索引)會導(dǎo)致未定義的行為。通常STL自己不會拋出異常。如果STL容器所調(diào)用的使用者自定操作拋出異常,會導(dǎo)致各不相同的行為。

容器的共通操作如表6.1

6.2 Vector

6.2-1 vector的容量之所以重要,有以下兩個原因:

(1)一旦內(nèi)存重新配置,和vector元素相關(guān)的所有references、pointers、iterators都會失效;

(2)內(nèi)存重新配置很耗時間(安插就可能導(dǎo)致重新配置)。

vectors的容量不會縮減,我們便可確定,即使刪除元素,其references、pointers、iterators也會繼續(xù)有效,繼續(xù)指向動作發(fā)生前的位置。然而安插操作卻可能是references、pointers、iterators失效(內(nèi)存重新配置)。

6.3 Deques

6.3-1 deque不支持對容量和內(nèi)存重分配時機的控制。不過,deque的內(nèi)存重分配優(yōu)于vector,因為其內(nèi)部結(jié)構(gòu)顯示,deque不必在內(nèi)存重分配時復(fù)制所有元素。參見《STL源碼剖析》

6.5 Sets 和 Multisets(紅黑樹)

有兩種方式可以定義排序準(zhǔn)則:

(1)例如: std::set<int, std::greater<int> > coll;

這種情況下,排序準(zhǔn)則就是型別的一部分。因此型別系統(tǒng)確保“只有排序準(zhǔn)則相同的容器才能被合并”。這是排序準(zhǔn)則的通常指定法。更精確地說,第二參數(shù)是排序準(zhǔn)則的型別,實際的排序準(zhǔn)則是容器所產(chǎn)生的函數(shù)對象(function object,或稱functor)。為了產(chǎn)生它,容器構(gòu)造函數(shù)會調(diào)用“排序準(zhǔn)則型別”的default構(gòu)造函數(shù)。

1. 元素比較動作只能用于型別相同的容器。換言之,元素和排序準(zhǔn)則必須有相同的型別,否則編譯時期會產(chǎn)生型別方面的錯誤。如下:

std::set<float> c1; // sorting criterion -> std::less<> std::set<float, std::greater<float> > c2; // sorting criterion -> std::less<> ... if (c1 == c2) // ERROR:different types {... }

2. 我們也可以使用自定之排序準(zhǔn)則,這里同時也是把仿函數(shù)當(dāng)做排序準(zhǔn)則

#include <iostream> #include <string> #include <set> #include <algorithm> using namespace std;class Person {public:string firstname() const;string lastname() const;.... }; /* class for function predicate * - operator() returns whether a person is less than another person */ class PersonSortCriterion {public:bool operator() (const Person& p1, const Person& P2) const{ /* a person is less than another person* - if the last name is less* - if the last name is equal and the first name is less*/return p1.lastname() < p1.lastname() ||(!(p2.lastname() < p1.lastname()) &&p1.firstname() < p2.firstname());} };int main() {// declare set type with special sorting criteriontypedef set<Person, PersonSortCriterion> PersonSet;// create such a collection PersonSet coll;...// do something with the elements PersonSet::iterator pos;for(pos = coll.begin(); pos != coll.end(); ++pos){...}... }

(2)以構(gòu)造函數(shù)參數(shù)定義之
這種情況下,同一個型別可以運用不同的排序準(zhǔn)則(如下面程序例子),而排序準(zhǔn)則的初始值或狀態(tài)也可以不同。如果執(zhí)行期才獲得排序準(zhǔn)則,而且需要用到不同的排序準(zhǔn)則(但數(shù)據(jù)型別必須相同),此一方式可派上用場。

// 執(zhí)行期指定排序準(zhǔn)則,排序準(zhǔn)則不同,但型別相同: #include <iostream> #include <set> using namespace std;// type for sorting criterion template <class T> class RuntimeCmp {public:enum cmp_mode {normal, reverse};private:cmp_mode mode;public:// constructor for sorting criterion// - default criterion uses value normalRuntimeCmp(cmp_mode m = normal) : mode(m) { }// comparision of elementsbool operator() (const T& t1, const T& t2) const{return mode == normal ? t1 < t2 : t2 < t1;}// comparision of sorting criteriabool operator== (const RuntimeCmp& rc){return mode == rc.mode;} };// type of a set that uses this sorting criterion typedef set<int, RuntimeCmp<int> > IntSet;// forward declaration void fill(IntSet& set);int main() {// create, fill, and print set with normal element order// - uses default sorting criterion IntSet coll1;fill(coll1);print_elements(coll1, "coll1 :");// create sorting criterion with reverse element orderRuntimeCmp<int> reverse_order(RuntimeCmp<int>::reverse);// create, fill, and print set with reverse element order IntSet coll2(reverse_order);fill(coll2);print_elements(coll2, "coll2 :");// assign elements AND sorting criterioncoll1 = coll2;coll1.insert(3);print_elements(coll1, "coll1 :");// just to make sure...if(coll1.value_comp() == coll2.value_comp()){cout << "coll1 and coll2 have same sorting criterion" << endl;}else{cout << "coll1 and coll2 have different sorting criterion" << endl;} }void fill(IntSet& set) {// fill insert elements in random orderset.insert(4);set.insert(7);set.insert(5);set.insert(1);set.insert(6);set.insert(2);set.insert(5); }輸出: coll1:1 2 4 5 6 7 coll2:7 6 5 4 2 1 coll1:7 6 5 4 3 2 1 coll1 and coll2 have same sorting criterion

1. 在這個程序中,RuntimeCmp<> 是一個簡單的template,提供“執(zhí)行期間面對任意型別定義一個排序準(zhǔn)則”的泛化能力。其default構(gòu)造函數(shù)采用默認(rèn)值normal,按升序排序;你也可以將RuntimeCmp<>::reverse傳遞給構(gòu)造函數(shù),便能按降序排序。

2. 注意,coll1 和 coll2 擁有相同的型別(包括元素的型別int,和排序準(zhǔn)則的型別RuntimeCmp),該型別即fill() 函數(shù)的參數(shù)型別。再請注意,assignment操作符不僅賦值了元素,也賦值了排序準(zhǔn)則(否則任何一個賦值操作豈不會輕易危及排序準(zhǔn)則)。

3. 請注意,排序準(zhǔn)則也被用于元素相等性檢驗工作,當(dāng)采用缺省排序準(zhǔn)則時,兩元素的相等性檢驗語句如下:

if (!(elem1 < elem2 || elem2 < elem1))

這樣做的好處有三:

a. 只需傳遞一個參數(shù)作為排序準(zhǔn)則;

b. 不必針對元素型別提供operator==;

c. 你可以對“相等性”有截然相反的定義,不過當(dāng)心造成混淆。

6.6 Maps? 和 Multimaps

Maps和Multimaps 的元素型別key 和 T, 必須滿足一下兩個條件:

(1)key/value 必須具備assignable(可賦值的)和copyable(可復(fù)制的)性質(zhì);

(2)對排序準(zhǔn)則而言,key必須是comparable(可比較的)。

Maps,Multimaps的排序準(zhǔn)則定義和Sets,Multisets類似。同樣,元素的比較動作也只能用于型別相同的容器。換言之,容器的key,value,排序準(zhǔn)則都必須有相同的型別,否則編譯期會產(chǎn)生型別方面的錯誤。

再次強調(diào),更易型算法不得用于關(guān)聯(lián)式容器,針對關(guān)聯(lián)式容器,提供的迭代器類型為const iterator。

6.7 其他STL容器

6.7-1

STL 是個框架,除了提供標(biāo)準(zhǔn)容器,它也允許你使用其他數(shù)據(jù)結(jié)構(gòu)作為容器。你可以使用字符串或數(shù)組作為STL容器,也可以自行撰寫特殊容器以滿足特性需求。如果你自行撰寫容器,仍可從諸如排序、合并等算法中受益。這樣的框架正是“開放性封閉(Open-Closed)”原則的極佳范例:允許擴展,謝絕修改。

下面是是你的容器“STL化”的三種不同方法:

(1)The invasive approach(侵入性作法) 直接提供STL容器所需接口。特別是諸如begin() 和 end() 之類的常用函數(shù)。這種作法需以某種特定方式編寫容器,所以是侵入性的。

以string為例:

Strings可被視為以字符為元素的一種容器;字符構(gòu)成序列,你可以在序列上來回移動遍歷。因此,標(biāo)準(zhǔn)的string類別提供了STL容器接口。Strings也提供成員函數(shù)begin()和end(),返回隨機存取迭代器,可用來遍歷整個string。

(2)The noninvasive approach(非侵入性作法)

由你撰寫或提供特殊迭代器,作為算法和特殊容器間的界面。此一作法是非侵入性的,它所需要的只是“遍歷容器所有元素”的能力——這是任何容器都能以某種形式展現(xiàn)的能力。

以Array為例:

Arrays可被視為一種STL容器,但array并不是類別,所以不提供begin() 和 end() 等成員函數(shù),也不允許存在任何成員函數(shù)。在這里,我們只能采用非侵入性作法或包裝法。采取非侵入性作法很簡單,你只需要一個對象,它能夠透過STL迭代器接口,遍歷數(shù)值的所有元素。事實上這樣的對象早已存在,它就是普通指針。STL設(shè)計之初就決定讓迭代器擁有和普通指針相同的接口,于是你可以將普通指針當(dāng)成迭代器來使用。

(3)The wrapper approach(包裝法) 將上述兩種方法加以組合,我們可以寫一個外套類別來包裝任何數(shù)據(jù)結(jié)構(gòu),并顯示出與STL容器相似的接口。

6.7-2 Hash Tables

Hash Tables數(shù)據(jù)結(jié)構(gòu)可用于群集身上,非常重要,卻因為提議太晚,未能包含于C++標(biāo)準(zhǔn)程序庫——“我們必須在某個時間點中止引入新功能,開始關(guān)注細(xì)節(jié),否則工作永無止境”。 Hash tables相關(guān)內(nèi)容可以參見《STL源碼剖析》。

6.9-1

表6.33提供了一份STL容器能力一覽表。

------------------------------------------------------------------------------------------------------------------------------------
7 STL迭代器
STL迭代器相關(guān)詳細(xì)內(nèi)容參見《STL源碼剖析》,這里只提幾點注意事項:
(1)迭代器是一種“能夠遍歷某個序列內(nèi)的所有元素”的對象(注意,迭代器是一種對象)。
(2)注意,Input迭代器只能讀取元素一次。如果你復(fù)制Input迭代器,并使原Input迭代器和新產(chǎn)生的副本都先前讀取,可能會遍歷到不同的值(故而,如果兩個Input迭代器占用同一個位置,則兩者相等,但這并不意味它們存取元素時能夠傳回相同的值)。
(3)Output迭代器和Input迭代器相反,其作用是將元素值一個個寫入。也就是說,你只能一個元素一個元素地賦新值,而且不能使用Output迭代器對同一序列進(jìn)行兩次遍歷。這就好像將元素值寫到“黑洞”里去,如果在相同位置上對著同一個“黑洞”進(jìn)行第二次寫入,不能確保這次寫入的值會覆蓋前一個值。
(4) 面對Output迭代器,我們無需檢查是否抵達(dá)序列尾端,便可直接寫入數(shù)據(jù)。事實上由于Output迭代器不提供比較操作,所以你不能將Output迭代器和尾端迭代器相比較。
(5)和Input迭代器及Output迭代器不同,Forward迭代器能多次指向同一群集中的同一元素,并能多次處理同一元素。同時,你必須在提領(lǐng)數(shù)據(jù)之前確保它有效。
(6)只有在面對Random Access Iterator時,你才能以 operator< 作為循環(huán)結(jié)束與否的判斷準(zhǔn)則(只有Random Access Iterator提供了 operator< 操作符),如“pos < coll.end() -1;”;否則,一般情況下我們使用不等號“pos != coll.end();”。
(7)盡可能優(yōu)先選用前置式遞增操作符(++iter)而不是后置式遞增操作符(iter++),因為前者性能更好,參見C++小語法。
(8)迭代器的遞增和遞減操作有個問題。一般而言你可以遞增或遞減臨時迭代器,但對于vectors和strings就不行,如下例子:

std::vector<int> coll; ... // sort, starting with the second element if (coll.size() > 1) {sort(++coll.begin(), coll.end() ); }

通常編譯sort時會失敗。但如果你換用deque取代vector,就可以通過編譯。有時vector也可以通過編譯——取決于vector的具體實作。
這種問題的產(chǎn)生原因是,vector的迭代器通常被實作為一般指針。要知道,C++不允許你修改任何基本型別(包括指針)的臨時值(典型的右值),但對于struct和class則允許。因為如果迭代器被實作為一般指針,編譯會失敗;如果被實作為class,則編譯可以成功。deques、lists、sets和maps總是能夠通過編譯,因為它們的迭代器不可能被實作為一般指針。至于vector,就要取決于實作手法了。
(9)Reverse(逆向)迭代器轉(zhuǎn)換前后迭代器的邏輯位置發(fā)生了變化,具體參見《STL源碼剖析》。容器的成員函數(shù)rbegin()和rend()各傳回一個Reverse迭代器,它們就想begin()和end()一樣,共同定義一個半開區(qū)間。
(10)有三種插入迭代器:back inserters、front inserters和general inserters。general inserters(或稱general insert iterator)根據(jù)兩個參數(shù)而初始化:1. 容器 2. 待安插位置。迭代器內(nèi)部以“待安插位置”為參數(shù),調(diào)用成員函數(shù)insert()。general inserters對于所有標(biāo)準(zhǔn)容器均適用,因為所有容器都有insert()成員函數(shù)。然而對關(guān)聯(lián)式容器(sets和maps)而言,安插位置只是個提示,因為在這兩種容器中,元素的真正位置視其實值(或鍵值)而定,安插位置如果使用不當(dāng)反而可能導(dǎo)致比較糟糕的性能。

?------------------------------------------------------------------------------------------------------------------------------------
8 仿函數(shù)(functors)——又名函數(shù)對象,function objects
仿函數(shù)的詳細(xì)信息參見《STL源碼剖析》以及上一篇博客C++標(biāo)準(zhǔn)程序庫筆記之一 5.9-1 ,這里只提幾點需要特別注意的地方:
(1)仿函數(shù)是passed by value(傳值),不是passed by reference(傳址),因此,算法并不會改變隨參數(shù)而來的仿函數(shù)的狀態(tài)。例如下面產(chǎn)生的兩個序列都以1開始:

IntSequence seq(1); // integral sequence starting with value 1 // insert sequence beginning with 1 generate_n(back_inserter(coll), 9, seq);// insert sequence beginning with 1 again generate_n(back_inserter(coll), 9, seq);

將仿函數(shù)以by value方式傳遞(而非by reference方式傳遞)的好處是,你可以傳遞常量或暫時表達(dá)式。如果不這么設(shè)計,你就不可能傳遞IntSequence(1)這樣的表達(dá)式。至于缺點就是,你無法改變仿函數(shù)的狀態(tài)。算法當(dāng)然可以改變仿函數(shù)的狀態(tài),但你無法存取并改變其最終狀態(tài),因為你所改變的只不過是仿函數(shù)的副本而已。然而有時候我們的確需要存取最終狀態(tài),因此,問題在于如何從一個算法中獲得結(jié)果。 有兩個辦法可以從“運用了仿函數(shù)”的算法中獲取“結(jié)果”或“反饋”:

1. 以by reference的方式傳遞仿函數(shù);

2. 運用for_each()算法的返回值。

1. 為了能夠以by reference 方式傳遞仿函數(shù),你只需要在調(diào)用算法時,明白標(biāo)示仿函數(shù)型別是個reference型別,如下:

#include <iosteam> #include <list> #include <algorithm> using namespace std;class IntSequence {private:int value;public:// constructorIntSequence(int initialValue): value(initialValue) { }// function call, 無參int operator() (){return value++;} };int main() {list<int> coll;IntSequence seq(1); // integral sequence starting with 1// insert values from 1 to 4// - pass function object by reference(傳址)// so that it will continue with 5// 傳址,改變了仿函數(shù)的狀態(tài)generate_n<back_insert_iterator<list<int> >,int, IntSequence&>(back_inserter(coll), // start4, // number of elementsseq); // generates values print_elements(coll);// insert values from 42 to 45// 傳值,不會改變原來仿函數(shù)的狀態(tài),改變的是仿函數(shù)的副本的狀態(tài)generate_n(back_inserter(coll), 4, IntSequence(42));print_elements(coll);// continue with first sequence// - pass function object by value(傳值)// so that it will continue with 5 again// 傳值,不會改變原來仿函數(shù)的狀態(tài),改變的是仿函數(shù)的副本的狀態(tài)generate_n(back_inserter(coll), 4, seq);print_elements(coll);// continue with first sequence again// 傳值,不會改變原來仿函數(shù)的狀態(tài),改變的是仿函數(shù)的副本的狀態(tài)generate_n(back_inserter(coll), 4, seq);print_elements(coll);}輸出: 1 2 3 4 1 2 3 4 42 43 44 45 1 2 3 4 42 43 44 45 5 6 7 8 1 2 3 4 42 43 44 45 5 6 7 8 5 6 7 8

2. for_each() 有一個其他算法概莫有之的絕技,那就是它可以返回仿函數(shù)。這樣,你就可以通過for_each() 的返回值來獲取仿函數(shù)的狀態(tài)了。

#include <iostream> #include <vector> #include <algorithm> using namespace std;// function object to process the mean value class MeanValue {private:long num; // number of elementslong sum; // sum of all element valuespublic:// constructorMeanValue() : num(0), sum(0) { }// "function call"// - process one more element of the sequencevoid operator() (int elem){num++; // increment countsum += elem; // add value }// return mean valuedouble value(){return static_cast<double>(sum) / static_cast<double>(num);} };int main() {vector<int> coll;// insert elements from 1 to 8// for_each返回一個仿函數(shù),保存在mv里面,記錄了仿函數(shù)(的副本)改變之后的狀態(tài)MeanValue mv = for_each(coll.begin(), coll.end(), MeanValue());cout << "mean value:" << mv.value() << endl; }

(2)判斷式(Predicates),就是返回布爾值(可轉(zhuǎn)換為bool)的一個函數(shù)或仿函數(shù)。對STL而言,并非所有返回布爾值的函數(shù)都是合法的判斷式,看下面的例子:

#include <iostream> #include <list> #include <algorithm> using namespace std;class Nth // function object that returns true for the nth call {private:int nth; // call for which to return trueint count; // call counterpublic:Nth(int n) : nth(n), count(0) { }bool operator() (int){return ++count == nth;} };int main() {list<int> coll;// insert elements from 1 to 9for(int i = 1; i <= 9; ++i){coll.push_back(i);}print_elements(coll, "coll:");// remove third elementlist<int>::iterator pos;pos = remove_if(coll.begin(), coll.end(), Nth(3));coll.erase(pos, coll.end());print_elements(coll, "nth removed:"); }輸出: coll: 1 2 3 4 5 6 7 8 9 nth removed: 1 2 4 5 7 8 9

有兩個(而不是一個)元素——也就是第3個和第6個元素——被移除了!為什么會這樣?因為該算法的一般實作版本,會于算法內(nèi)部保留判斷式的一份副本:

template <class ForwIter, class Predicate> ForwIter std::remove_if(ForwIter beg, ForwIter end, Predicate op) {beg = find_if(beg, end, op); if (beg == end){return beg;}else{ForwIter next = beg;return remove_copy_if(++next, end, beg, op);} }

1. remove_if 傳進(jìn)op,此時會復(fù)制一份op的副本供內(nèi)部使用(副本1);

2. find_if 傳進(jìn)副本1, 同樣會根據(jù)副本1 復(fù)制一份副本2,供find_if內(nèi)部使用;

3. 調(diào)用remove_copy_if時,可見的是副本1,此時的副本1并未被修改過(因為find_if使用的是根據(jù)副本1復(fù)制而來的副本2)。

也即,這個算法使用find_if來搜尋應(yīng)被移除的第一個元素。然而,接下來它使用傳進(jìn)來的判斷式op的副本去處理剩余元素。這時原始狀態(tài)下的Nth再一次被使用,因而會移除剩余元素中的第3個元素,也就是整體的第6個元素。

這種行為不能說是一種錯誤,因為C++ Standard 并未明定判斷式是否可被算法復(fù)制一份副本。因此,為了獲得C++標(biāo)準(zhǔn)程序庫的保證行為,你不應(yīng)該傳遞一個“行為取決于被拷貝次數(shù)或被調(diào)用次數(shù)”的仿函數(shù)。也就是說,如果你以兩個相同參數(shù)來調(diào)用一個單參數(shù)判斷式,該判斷式應(yīng)該總是返回相同結(jié)果。換句話說,判斷式不應(yīng)該因為被調(diào)用而改變自身狀態(tài);判斷式的副本應(yīng)該和其正本有著相同狀態(tài)。要做到這一點,你一定得保證不能因為函數(shù)調(diào)用而改變判斷式的狀態(tài),你應(yīng)當(dāng)將 operotor() 聲明為const成員函數(shù)。

上面的尷尬結(jié)果可以避免,只要使用另一種辦法來代替find_if():

template <class ForwIter, class Predicate> ForwIter std::remove_if(ForwIter beg, ForwIter end, Predicate op) {// 不適用STL算法find_if,不會再額外復(fù)制多一份op的副本while (beg != end && !op(*beg) ){++beg;}if (beg == end){return beg;}else{ForwIter next = beg;return remove_copy_if(++next, end, beg, op);} }

就我所知,目前的STL實作品中,只有remove_if()算法有這個問題。如果你換用remove_copy_if() ,就一切正常。然而如果考慮到可移植性,你就永遠(yuǎn)不該依賴任何實作細(xì)節(jié),而應(yīng)該總是將判斷式的 operator() 聲明為const成員函數(shù)。

(3)C++標(biāo)準(zhǔn)程序庫提供了許多預(yù)定義仿函數(shù),表8.1, 要使用這些仿函數(shù),必須包含頭文件<functional>. 同時也提供了各種類型的函數(shù)配接器,使用組合使用函數(shù)配接器,可以構(gòu)造出非常復(fù)雜的表達(dá)式。參見《STL源碼剖析》

------------------------------------------------------------------------------------------------------------------------------------

9 STL 算法 —— STL Algorithms

STL算法的詳細(xì)信息參見《STL源碼剖析》,這里只提幾點需要特別注意的地方:

(1)STL算法采用覆蓋模式而非安插模式。所以調(diào)用者必須保證目標(biāo)區(qū)間擁有足夠的元素空間。當(dāng)然,你也可以運用特殊的安插型迭代器將覆蓋模式改變?yōu)榘膊迥J?#xff1b;

(2)再次強調(diào),判斷式不應(yīng)該在函數(shù)調(diào)用過程中改變其自身狀態(tài);

(3)再次強調(diào),關(guān)聯(lián)式容器的元素被視為常數(shù),惟其如此,你才不會在變動元素的時候有任何可能違反整個容器的排序準(zhǔn)則。因此,你不可以將關(guān)聯(lián)式容器當(dāng)做變動性算法的目標(biāo)區(qū)間;

(4)注意,移除算法只是在邏輯上移除元素,手段是:將不需被移除的元素往前覆蓋應(yīng)被移除的元素。因此它并不改變操作區(qū)間內(nèi)的元素個數(shù),而是返回邏輯上的新終點位置。至于是否使用這個位置進(jìn)行諸如“實際移除元素”之類的操作,那是調(diào)用者的事情。任何算法,都不能(也無法)通過迭代器移除元素。

(5)Lists沒有提供隨機存取迭代器,所以不可以對它使用排序算法。然而lists本身提供了一個成員函數(shù)sort(),可用來對其元素排序。

(6)重排元素(Shuffling, 攪亂次序)

void random_suffle (RandomAccessIterator beg, RandomAccessIterator end) void random_suffle (RandomAccessIterator beg, RandomAccessIterator end, RandomFunc& op)

注意,op是一個non-const reference。所以你不可以將暫時數(shù)值或一般函數(shù)傳進(jìn)去(臨時變量、函數(shù)返回值都是典型的右值,只有const reference可以指向它們)。
random_shuffle()的op參數(shù)需要使用non-const reference的原因:必須如此,因為典型的隨機數(shù)產(chǎn)生器擁有一個局部狀態(tài)(locate state)。舊式C函數(shù)如rand()是將其局部狀態(tài)存儲在某個靜態(tài)變量中。但是這有一些缺點,例如這種隨機數(shù)發(fā)生器本質(zhì)上對于多線程(multi-threads)而言就不安全,而且你也不可能擁有兩個各自獨立的隨機數(shù)流(streams)。如果使用仿函數(shù),其區(qū)域狀態(tài)被封裝為一個或多個成員變量,那么就有了比較好的解決方案。這樣一來,隨機數(shù)產(chǎn)生器就不可能具備常數(shù)性,否則何以改變內(nèi)部狀態(tài),何以產(chǎn)生新的隨機數(shù)呢?不過你還是可以用by value方式傳遞隨機數(shù)產(chǎn)生器,為什么非要以by non-const reference方式傳遞呢?是這樣的,如果這么做(采用by value),每次調(diào)用都會在內(nèi)部復(fù)制一個隨機數(shù)產(chǎn)生器及其狀態(tài),結(jié)果,每次你傳入隨機數(shù)產(chǎn)生器,所得的隨機數(shù)序列都一樣,那又有何隨機可言?所以,必須以by non-const reference方式傳遞op參數(shù)。

------------------------------------------------------------------------------------------------------------------------------------
10 特殊容器
(1)兩大類:
1. 容器配接器(container adapters):Stacks(棧),Queues(隊列),Priority queues(優(yōu)先隊列),參見《STL源碼剖析》
2. bitset特殊容器:Bitsets造出一個內(nèi)含(bits)或布爾(boolean)值且大小固定的array。當(dāng)你需要管理各式標(biāo)志(flags),并以標(biāo)志的任意組合在表現(xiàn)變量時,就可運用bitsets。C程序和傳統(tǒng)C++程序通常使用型別long來作為bits array,再通過&, |, ~等位操作符(bit operators)操作各個位。Class bitset的優(yōu)點在于可容納任意個數(shù)的位(但不能動態(tài)改變),并提供各項操作。例如你可以對某個特定位置賦值一個位,也可以將bitsets作為由0和1組成的序列,進(jìn)行讀寫。
注意,你不可以改變bitset內(nèi)位的數(shù)量。這個數(shù)量的具體值是由template參數(shù)決定的。如果你需要一個可變長度的位容器,可考慮使用vector<bool>。
Class bitset定義于頭文件<bitset>之中, 其中的class bitset是個template class,有一個template參數(shù),用來指定位的數(shù)量:

#include <bitset> namespace std {template <size_t Bits>class bitset; }

?在這里,template參數(shù)并不是一個型別,而是一個不帶正負(fù)號的整數(shù)。注意,如果template參數(shù)不同,具現(xiàn)化所得的template型別就不同。換句話說,你只能針對位個數(shù)相同的bitsets進(jìn)行比較和組合。
(2)Bitsets運用實例
1. 將Bitsets當(dāng)做一組標(biāo)志
這個例子展示如何運用bitsets來管理一組標(biāo)志。每個標(biāo)志都有一個由枚舉型(enum)定義出來的值。該枚舉值就表示位在bitset中的位置。舉個例子,這些bits可以代表顏色,那么每一個枚舉值都代表一種顏色。通過運用bitset,你可以管理一個變量,其中包含顏色的任意組合:

#include <bitset> #include <iostream> using namespace std;int main() {/* enumeration type for the bits* - each bit represents a color*/enum Color {red, yellow, green, blue, white, black,……numColors };// create bitset for all bits/colorsbitset<numColors> usedColors;// set bits for two colorsusedColors.set(red); // 設(shè)置red(0號位置)上的bit位usedColors.set(blue); // 設(shè)置blue(3號位置)上的bit位// print some bitset datacout << "bitfield of used colors:" << usedColors << endl;cout << "number of used colors:" << usedColors.count() << endl;cout << "bitfield of unused colors:" << ~usedColors << endl;// if any color is usedif (usedColors.any()){// loop over all colorsfor (int c = 0; c < numColors; ++c){//if the actual color is usedif (usedColors[(Color)c]){....}}} }

2. 在I/O中利用Bitsets表示二進(jìn)制
Bitsets一個強有力的特性就是可以在整數(shù)值和位序列之間相互轉(zhuǎn)化,只要很簡單地產(chǎn)生一個臨時的bitset就可以辦到:

#include <bitset> #include <iostream> #include <string> #include <limits> using namespace std;int main() {/* print some numbers in binary representation*/cout << "267 as binary short:" << bitset<numeric_limits<unsigned short>::digits>(267) << endl;cout << "267 as binary long:" << bitset<numeric_limits<unsigned long>::digits>(267) << endl;cout << "10, 000, 000 with 24 bits:" << bitset<24>(1e7) << endl;/* transform binary representation into integral number*/cout << "\"1000101011\" as number:" << bitset<100>(string("1000101011")).to_ulong() << endl; }// 程序可能輸出如下(具體內(nèi)容視平臺上的short和long的位數(shù)量而定) 267 as binary short: 0000000100001011 267 as binary long: 00000000000000000000000100001011 10, 000, 000 with 24 bits: 100110001001011010000000 "1000101011" as number: 555

Class Bitsets更詳細(xì)的信息參見相關(guān)源碼和《STL源碼剖析》

?

轉(zhuǎn)載于:https://www.cnblogs.com/yyxt/p/5017942.html

總結(jié)

以上是生活随笔為你收集整理的《C++标准程序库》笔记之二的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。