1 #include <iostream> 2 #include <string> 3 4 int main() 5 { 6 using namespace std; 7 8 cout << "1 --- string(const char* s):將string對象初始化爲s指向的C風格字符串" << endl; 9 string one("benxintuzi_1"); 10 cout << "one = " << one << endl; // 重載了 << 11 12 cout << "2 --- string(size_type n, char c):以字符c初始化包含n個字符的string對象" << endl; 13 string two(20, '$'); 14 cout << "two = " << two << endl; 15 16 cout << "3 --- string(const string& str):複製構造函數初始化" << endl; 17 string three(one); 18 cout << "three = " << three << endl; 19 20 cout << "4 --- string():默認構造函數" << endl; 21 string four; 22 four += one; // 重載了 += 23 four = two + three; // 重載了 + 24 cout << "four = " << four << endl; 25 26 cout << "5 --- string(const char* s, size_type n):用s指向的C風格字符串的前n個字符初始化string對象" << endl; 27 char s[] = "benxintuzi"; 28 string five(s, 5); 29 cout << "five = " << five << endl; 30 31 cout << "6 --- string(Iter begin, Iter end):用[begin, end)區間內的字符初始化string對象" << endl; 32 string six(s + 2, s + 5); 33 cout << "six = " << six << endl; 34 35 cout << "7 --- string(const string& str, string size_type pos = 0, size_type n = npos):將string對象初始化爲str中從pos開始的n個字符" << endl; 36 string seven(four, 5, 2); 37 cout << "seven = " << seven << endl; 38 39 /* 40 cout << "8 --- string(string&& str) noexcept:將string對象初始化爲str,並可能修改str,移動構造函數[C++11新特性]" << endl; 41 42 cout << "9 --- string(initializer_list<char>il:將string對象初始化爲初始化列表il中的字符[C++11新特性]" << endl; 43 string nine = {'t', 'u', 'z', 'i'}; // or string nine{'t', 'u', 'z', 'i'}; 44 cout << "nine = " << nine << endl; 45 */ 46 }
對於 C 風格字符串,有3種輸入方式ios |
char info[100];算法 cin >> info; // 從流中讀一個單詞存放到info中數組 cin.getline(info, 100); // 從流中讀入一行存放到info,刪除流中的\n安全 cin.get(info, 100); // 從流中讀入一行存放到info,保留流中的\nide |
對於 string 對象,有2種輸入方式函數 |
string stuff;spa cin >> stuff; // 從流中讀取一個單詞操作系統 getline(cin, stuff); // 從流中讀取一行,刪除流中的\n指針 |
getline能夠指定用於分割單詞的分隔符,將其放到第三個參數中,如:code
cin.getline(info, 100, ‘:’); // C 風格字符串使用這種形式
getline(stuff, ‘:’); // string 類使用這種形式
string與傳統 C 風格字符串的比較:
使用string類輸入時,不須要具體指定輸入的字符個數,其能夠自動匹配字符串大小,使用C風格字符串必須顯示指定,由於C風格字符串使用的是istream類的方法,而string版本使用的是獨立的函數,所以形式上有所區別,這種區別在其餘運算符上也有體現,如>>操做符:
C 風格字符串使用:cin.operator>>(fname),而string風格爲:operator(cin, fname);
說明:
string版本的getline()在以下三種狀況下結束讀取:
1 到達文件末尾:此時輸入流中的eofbit置位;
2 遇到分割符:默認爲\n,此時,刪除流中的\n;
3 讀取的字符個數達到最大值[min(string::npos, 空閒內存)],此時將輸入流的failbit置位。
在輸入流中有一個統計系統,用於跟蹤流的狀態:
1 檢測到文件末尾,設置eofbit寄存器;
2 檢測到錯誤,設置failbit寄存器;
3 檢測到沒法識別的故障,設置badbit寄存器;
4 檢測到一切順利,設置goodbit寄存器。
string 類對所有 6 個關係運算符進行了重載,對於每一個關係運算符,又進行了3種重載,分別是:
string 對象與 string 對象;
string 對象 C 風格字符串;
C 風格字符串與 string 對象。
智能指針是行爲相似於指針,但倒是類。其能夠幫助管理動態分配的內存,有三種可選:分別爲auto_ptr\unique_ptr\shared_ptr。auto_ptr是C++ 98提供的,C++ 11已經拋棄了(雖然已經被拋棄,可是仍然被大量使用)。
小知識: 爲什麼新標準要放棄auto_ptr?理由以下: 假設以下賦值: auto_ptr<string> ps1(new string(「benxintuzi」)); auto_ptr<string> ps2; ps2 = ps1; 若是ps2和ps1是普通指針,那麼他們將同時指向一塊堆內存,那麼auto_ptr類型的指針若同時指向一塊堆內存,那麼就會調用兩次delete,這個太可怕了,所以,auto_ptr採用的策略是若是發生賦值,那麼就會令ps1爲0,一樣unique_ptr也是採用這種策略,可是更加嚴格。 以下,當ps2 = ps1,即ps1置空時,若是發生調用*ps1,那麼至關於調用了一個空指針指向的對象,在auto_ptr狀況下,能夠編譯經過,在運行時會出錯;但在unique_ptr狀況下,不會讓你經過編譯的。 結論就是unique_ptr比auto_ptr更安全一些。
shared_ptr對於每一個堆對象,其維護一個指向同一對象的引用計數,只有當引用計數爲0時才調用delete,所以能夠很好地支持智能指針賦值操做。 |
要建立智能指針,必須包含頭文件memory,而後使用模板語法實例化所需類型的指針,例如auto_ptr包含以下構造函數:
template<class X> class auto_ptr
{
public:
// throw()意味着不引起異常【C++ 11也將throw()拋棄了】
explicit auto_ptr(X* p = 0) throw();
...
}
所以,智能指針的使用很是簡單,只須要用特定類型的指針初始化便可,如:
auto_ptr<double> pd(new double);
說明:
智能指針都放在名稱空間std中(注意shared_ptr和unique_ptr都是C++ 11新增的,舊時的編譯器可能不支持)。
1 #include <iostream> 2 #include <string> 3 #include <memory> 4 5 class Report 6 { 7 public: 8 Report(const std::string s) : str(s) 9 { 10 std::cout << "Object created!" << std::endl; 11 } 12 ~Report() 13 { 14 std::cout << "Object deleted!" << std::endl; 15 } 16 void comment() const 17 { 18 std::cout << "str = " << str << std::endl; 19 } 20 21 private: 22 std::string str; 23 }; 24 25 int main() 26 { 27 std::auto_ptr<Report> pa(new Report("using auto_ptr")); 28 pa->comment(); 29 30 std::shared_ptr<Report> ps(new Report("using shared_ptr")); 31 ps->comment(); 32 33 std::unique_ptr<Report> pu(new Report("using unique_ptr")); 34 pu->comment(); 35 36 return 0; 37 } 38 39 /** output */ 40 Object created! 41 str = using auto_ptr 42 Object created! 43 str = using shared_ptr 44 Object created! 45 str = using unique_ptr 46 Object deleted! 47 Object deleted! 48 Object deleted!
注意:
絕對不要將非堆內存指針賦予智能指針,不然雖然能夠經過編譯,可是狀況並不是老是樂觀的,這就至關於delete了非堆內存,容易引起難以發現的問題。
關於如何選擇合適的智能指針,以下給出參考建議:
若是須要多個指向同一堆對象的有效指針,那麼選擇使用shared_ptr。這種狀況包括:
1 有一個指針數組,而且使用一些輔助指針標識特殊元素,如標識最值元素;
2 兩個對象中都包含指向第三個對象的指針;
3 STL容器中的指針等,不少STL算法都支持複製和賦值操做。
若是程序不須要多個有效指針同時指向同一堆對象,那麼使用unique_ptr。若是須要將unique_ptr做爲右值時,可將其賦值給shared_ptr;在知足unique_ptr條件時,也可以使用auto_ptr,但unique_ptr彷佛是更好的選擇(若是編譯器不提供unique_ptr,可使用boost庫提供的scoped_ptr,其功能與unique_ptr相似)。
迭代器就是廣義的指針,可對其進行遞增操做和解引用操做等的對象。每一個容器類都定義了一個與之相關的迭代器,該迭代器是一個名爲iterator的typedef定義,做用域爲整個類。模板使算法獨立於數據類型,而迭代器使算法獨立於容器類型。
迭代器主要用於遍歷容器中的元素,其應該具備以下基本功能:
1 支持解引用操做
2 賦值操做
3 比較操做,如 ==、!=
4 自增操做
具有以上能力就差很少了。其實STL按照迭代器功能強弱定義了多種級別(5種)的迭代器,說明以下:
迭代器類型 |
說明 |
輸入迭代器 |
【讀容器】可來讀取容器中的元素,但可能不會修改容器中的元素。輸入迭代器必須能夠訪問容器中的全部元素,所以,支持++操做。 |
輸出迭代器 |
【寫容器】程序能夠修改容器中的元素值,但不能讀取容器中的元素。 |
正向迭代器 |
【讀或者寫容器】單向讀寫。 |
雙向迭代器 |
【雙向讀或者寫容器】雙向讀寫。支持自減運算符。 |
隨機訪問迭代器 |
【雙向讀或者寫容器】提供了附加的「跳步」訪問能力。 |
迭代器支持的操做含義:
表達式 |
說明 |
a + n、n + a |
指向a所指元素後的第n個元素 |
a - n |
指向a所指元素前的第n個元素 |
r += n、r -= n |
r = r + n、r = r – n |
a[n] |
*(a + n) |
b - a |
a ~ b區間的元素個數 |
a < b、a > b、a >= b、a <= b |
邏輯判斷 |
STL迭代器功能彙總:
總結:
迭代器支持的一般操做爲解引用讀或者寫;全部類型的迭代器都支持自增操做;自減操做只有雙向迭代器和隨機訪問迭代器支持;隨機訪問迭代器爲「跳步」訪問提供了可能。所以迭代器能力大小能夠表示以下:
隨機訪問迭代器 > 雙向迭代器 > 正向迭代器 > 輸入迭代器 == 輸出迭代器
迭代器是廣義的指針,而指針知足全部迭代器的要求。所以STL算法可使用指針來對基於指針的非STL容器進行操做。例如,可將STL算法用於數組:
若是要將一個double Receipts[100]進行排序,可使用STL的算法sort,但傳入的確是指針,如:
sort(Receipts, Receipts + 100);
STL提供了一些預約義的迭代器:copy()、ostream_iterator、istream_iterator。
假設有以下定義:
int casts[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
vector<int> dice(10);
copy(casts, casts + 10, dice.begin()); // 將[casts, casts + 10)範圍內的數據複製到dice.begin()開始的目標空間中
輸出流迭代器:
假設如今須要將數據複製到顯示器上,那麼須要一個表示輸出流的迭代器,STL爲咱們提供了ostream_iterator模板。該迭代器是一個適配器,可將全部的接口轉換爲STL使用的接口。咱們使用時必須包含頭文件iterator,而且作出以下聲明來建立該迭代器:
#include <iterator>
...
ostream_iterator<int, char> out_iter(cout, 「 」);
說明:
int: 表示發送到輸出流中的數據類型;
char: 表示輸出流使用的字符類型;
構造函數中,cout: 表示要使用的輸出流;「 」:表示所發送數據的分隔符;
能夠這樣使用迭代器:
*out_iter++ = 15; // 等價於cout << 15 << 「 」;
這條語句代表將」15 」賦予指針指向的位置,而後指針向前移動一個單位。
實現如上的copy動做爲:
copy(dice.begin(), dice.end(), out_iter)便可將dice容器的整個區間複製到顯示器中顯示。
固然也能夠建立匿名的迭代器,以下:
copy(dice.begin(), dice.end(), ostream_iterator<int, char>(cout, 「 」));
輸入流迭代器:
對於的輸入流迭代器爲istream_iter模板:
copy(istream_iterator<int, char>(cin), istream_iterator<int, char>(), ostream_iterator<int, char>(cout, 「 」));
說明:
int:要讀取的數據類型;
char:輸入流使用的數據類型;
構造函數中,第一個參數cin表示讀取由cin管理的輸入流;省略構造函數意味着輸入失敗,所以上述代碼從輸入流中讀取int型數據,直到文件末尾、類型不匹配或者出現其餘輸入故障爲止才輸出到屏幕上。
1 #include <iostream> 2 #include <iterator> 3 using namespace std; 4 5 int main() 6 { 7 copy(istream_iterator<int, char>(cin), istream_iterator<int, char>(), 8 ostream_iterator<int, char>(cout, " ")); 9 return 0; 10 } 11 12 /** output */ 13 1 2 3 4 5 14 1 2 3 4 5 __
除了istream_iterator和ostream_iterator外,頭文件iterator中還提供了一些專用的迭代器,如:reverse_iterator、back_insert_iterator、front_insert_iterator和insert_iterator。
reverse_iterator執行遞增時,指針實際上發生遞減操做。例如:vector類中的rbegin()和rend()分別返回指向最後一個元素下一位置的指針、指向第一個元素的指針。
強調:
雖然rbegin()和end()返回的指針雖然指向同一個位置,可是類型不一樣,前者類型是reverse_iterator,後者類型是iterator。
一樣要注意的是:對於反向迭代的解引用的具體實現實則是先遞減,再解引用。不然會出錯。試想一下,若是一個rp是rbegin()返回的,直接解引用將致使操做一個空指針。
1 #include <iostream> 2 #include <iterator> 3 #include <vector> 4 using namespace std; 5 6 /* 7 int main() 8 { 9 copy(istream_iterator<int, char>(cin), istream_iterator<int, char>(), 10 ostream_iterator<int, char>(cout, " ")); 11 return 0; 12 } 13 */ 14 15 int main() 16 { 17 int casts[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 18 vector<int> dice(10); 19 copy(casts, casts + 5, dice.begin()); // 將casts的前5個數據複製到dice中 20 21 ostream_iterator<int, char> out_iter(cout, " "); // 建立輸出流迭代器 22 copy(dice.begin(), dice.end(), out_iter); // 將dice中的所有元素複製到屏幕上輸出 23 24 cout << endl; 25 26 /* 隱式使用reverse_iterator */ 27 copy(dice.rbegin(), dice.rend(), out_iter); // 將dice中的所有元素反向輸出到屏幕上 28 29 cout << endl; 30 31 /* 顯式使用reverse_iterator */ 32 vector<int>::reverse_iterator ri; 33 for(ri = dice.rbegin(); ri != dice.rend(); ++ri) 34 cout << *ri << " : "; 35 cout << endl; 36 37 return 0; 38 } 39 40 /** output */ 41 1 2 3 4 5 0 0 0 0 0 42 0 0 0 0 0 5 4 3 2 1 43 0 : 0 : 0 : 0 : 0 : 5 : 4 : 3 : 2 : 1 :
在形式上,STL中的不少算法都和copy函數相似,以下爲三種插入迭代器操做:
back_insert_iterator將元素插入容器尾部;
front_insert_iterator將元素插入容器頭部;
insert_iterator將元素插入指定位置。
插入操做相比於copy操做而言,能夠自動增長空間,而copy確是靜態分配好的空間,即便不夠了也不會向操做系統去申請,默認狀況下會截斷後邊的數據,只複製容量容許的數據。使用插入迭代器時,使用容器類型做爲模板參數,使用容器變量做爲構造參數,以下所述:
back_insert_iterator<vector<int>> back_iter(dice);
對於insert_iterator,還須要一個指定插入位置的構造參數:
insert_iterator<vector<int>> insert_iter(dice, dice.begin());
容器用於存儲對象,其要求所存儲的對象必須具備相同的類型,並且該類型必須是可複製構造和可賦值的。通常基本類型和類類型都知足要求(固然除非你把這兩種函數聲明爲私有或保護類型的)。C++ 11中又添加了可複製插入和可移動插入要求。
容器類型是用於建立具體容器的模板。以前共有11個容器類型:deque/list/queue/priority_queue/stack/vector/map/multimap/set/multiset/biset,C++ 11新增了5個:forward_list/unordered_map/unordered_multimap/unordered_set/unordered_multiset。
何爲序列?
序列保證了元素將按特定的順序排列,不會在兩次迭代之間發生變化。序列還要求其元素嚴格按線性順序排列,即存在第一個/第二個/.../等。好比數組和鏈表都是序列,但分支結構就不是序列。
因爲序列中的元素具備特定的順序,所以能夠執行像插入元素到特定位置、刪除特定區間等操做。以下容器都爲序列容器:
vector |
vector是數組的一種類的表示,它提供了自動管理內存的功能,能夠動態改變vector的長度,增大或減少。vector是能夠反轉的,主要是經過rbegin()和rend()實現的,而且其返回的迭代器類型都是reverse_iterator。 |
deque |
雙端隊列。其實現相似於vector,支持隨機訪問。主要區別在於能夠從deque中開頭和結尾處插入和刪除元素,並且其時間複雜度固定。 |
list |
雙向鏈表。與vector相似,list也能夠反轉。主要區別是:list不支持隨機訪問。除此以外,list模板類還包括了鏈表專用的成員函數,以下所示: void merge(list<T, Alloc>& x): 將鏈表x與調用鏈表合併,而且已然有序。合併後的鏈表保存在調用鏈表中,x爲空。 void remove(const T& val): 從鏈表中刪除val的全部實例。 void sort(): 使用<運算符對鏈表進行排序,時間複雜度爲nlgn。 void splice(iterator pos, list<T, Alloc>x): 將鏈表x的內容插入到pos前邊,插入後x爲空。 void unique(): 將鏈表中連續的相同元素壓縮爲單個元素。 說明: insert()和splice()之間的主要區別在於:insert是插入原始區間的副本到目標地址,而splice是將原始區間轉移到目標地址。 C++ 11新增了容器類forward_list,實現了單鏈表,與之關聯的迭代器是正向迭代器而非雙向迭代器。 |
queue |
queue模板類是一個適配器類,如前所述,ostream_iterator模板也是一個適配器,可讓輸出流使用迭代器接口,一樣,queue模板類讓底層類(默認爲deque)展現出隊列接口。 queue模板的限制比deque更多。他不只不容許隨機訪問,並且不容許遍歷操做。其把使用限制在隊列基本操做上,如入隊、出隊、取隊首、判隊空等,具體以下: bool empty() const: 若是隊列爲空則返回true;不然返回false。 size_type size() const: 返回隊列中元素的數目。 T& front(): 返回指向隊首元素的引用。 T& back(): 返回指向隊尾元素的引用。 void push(const T& x): 在隊尾插入x。 void pop(): 刪除隊首元素。 說明: priority_queue與queue的主要區別在於:最大元素被移到隊首。其內部實現爲vector,能夠指定內部元素排序規則。 |
stack |
適配器類,使用vector實現,支持操做以下: bool empty() const: 若是棧爲空則返回true;不然返回false。 size_type size() const: 返回棧中元素的數目。 T& top(): 返回指向棧頂元素的引用。 void push(const T& x): 在棧頂插入x。 void pop(): 刪除棧頂元素。 |
array(C++ 11 新增) |
模板類array並不是STL容器,由於其長度是固定的。所以不能使用動態調整容器大小的函數如push_back()和insert()等,可是其定義了頗有用的成員函數,如operator[]()和at(),正如以前所述,許多標準的STL算法也可用於array對象,如:copy()和for_each()。 |
操做示例:
1 #include <iostream> 2 #include <list> 3 #include <iterator> 4 #include <algorithm> 5 using namespace std; 6 7 void PrintList(int n){ cout << n << " "; } 8 9 int main() 10 { 11 list<int> one(5, 2); // 5個2 12 cout << "list one: "; 13 for_each(one.begin(), one.end(), PrintList); 14 cout << endl; 15 16 list<int> two; 17 int stuff[3] = {2, 5, 9}; 18 two.insert(two.begin(), stuff, stuff + 3); 19 cout << "list two: "; 20 for_each(two.begin(), two.end(), PrintList); 21 cout << endl; 22 23 list<int> three(two); 24 three.insert(three.end(), one.begin(), one.end()); 25 cout << "list three: "; 26 for_each(three.begin(), three.end(), PrintList); 27 cout << endl; 28 29 cout << "merge one and three: "; 30 three.splice(three.begin(), one); 31 for_each(three.begin(), three.end(), PrintList); 32 cout << endl; 33 34 cout << "delete the duplicate elements: "; 35 three.unique(); 36 for_each(three.begin(), three.end(), PrintList); 37 cout << endl; 38 39 cout << "merge with another: four = "; 40 list<int> four(3, 8); 41 four.merge(three); 42 for_each(four.begin(), four.end(), PrintList); 43 cout << endl; 44 45 cout << "remove the value 8 in four: "; 46 four.remove(8); 47 for_each(four.begin(), four.end(), PrintList); 48 cout << endl; 49 50 return 0; 51 } 52 53 /** output */ 54 list one: 2 2 2 2 2 55 list two: 2 5 9 56 list three: 2 5 9 2 2 2 2 2 57 merge one and three: 2 2 2 2 2 2 5 9 2 2 2 2 2 58 delete the duplicate elements: 2 5 9 2 59 merge with another: four = 2 5 8 8 8 9 2 60 remove the value 8 in four: 2 5 9 2