STL STL提供了六大組件,彼此之間可以組合套用,這六大組件分別是:容器、演算法、迭代器、仿函數、適配器、空間配置器。 數據結構和容器管理:STL 提供了多種數據結構和容器,如向量(vector)、鏈表(list)、集合(set)、映射(map)等。這些容器可以幫助程式員方便地存儲和管理數據,根據需 ...
STL
STL提供了六大組件,彼此之間可以組合套用,這六大組件分別是:容器、演算法、迭代器、仿函數、適配器、空間配置器。
- 數據結構和容器管理:STL 提供了多種數據結構和容器,如向量(vector)、鏈表(list)、集合(set)、映射(map)等。這些容器可以幫助程式員方便地存儲和管理數據,根據需求進行動態調整和操作。
- 演算法和數據處理:STL 中提供了大量的演算法,如排序、查找、遍歷等,這些演算法可以直接應用於不同類型的容器,幫助程式員高效地對數據進行處理和操作。
- 迭代器和訪問控制:STL 中的迭代器提供了統一的訪問介面,使得程式員能夠方便地遍歷容器中的元素併進行讀寫操作。迭代器可以靈活地控制訪問範圍和方式,為數據訪問提供了高度的靈活性。
- 泛型編程:STL 的設計基於泛型編程思想,通過模板機制實現了通用的數據結構和演算法。這使得 STL 能夠適用於各種數據類型,並且支持用戶自定義類型的操作,從而提高了代碼的重用性和可擴展性。
- 性能優化:STL 中的容器和演算法都經過高度優化,使用 STL 可以藉助這些優化的實現來提高程式的執行效率和性能。
總的來說,STL 適用於需要數據結構、演算法和迭代器等功能的各種場景。它為 C++ 程式員提供了豐富的工具和功能,幫助他們更加高效地處理和操作數據,提高代碼的可讀性、重用性和性能。無論是簡單的數據存儲管理還是複雜的數據處理和演算法實現,STL 都是一個強大而實用的工具庫。
容器:各種數據結構,如vector、list、deque、set、map等,用來存放數據,從實現角度來看,STL容器是一種class template。
演算法:各種常用的演算法,如sort、find、copy、for_each。從實現的角度來看,STL演算法是一種function tempalte.
迭代器:扮演了容器與演算法之間的膠合劑,共有五種類型,從實現角度來看,迭代器是一種將operator* , operator-> , operator++,operator--等指針相關操作予以重載的class template. 所有STL容器都附帶有自己專屬的迭代器,只有容器的設計者才知道如何遍歷自己的元素。原生指針(native pointer)也是一種迭代器。
仿函數:行為類似函數,可作為演算法的某種策略。從實現角度來看,仿函數是一種重載了operator()的class 或者class template
適配器:一種用來修飾容器或者仿函數或迭代器介面的東西。
空間配置器:負責空間的配置與管理。從實現角度看,配置器是一個實現了動態空間配置、空間管理、空間釋放的class tempalte.
STL優點
-
STL 是 C++的一部分,因此不用額外安裝什麼,它被內建在你的編譯器之內。
-
STL 的一個重要特點是數據結構和演算法的分離。儘管這是個簡單的概念,但是這種分離使得 STL 變得非常通用。例如:在 STL 的 vector 容器中,可以放入元素、基礎數據類型變數、元素的地址;STL 的 sort() 排序函數可以用來操作 vector,list 等容器。
-
程式員可以不用思考 STL 具體的實現過程,只要能夠熟練使用 STL 就 OK 了。這樣他們就可以把精力放在程式開發的別的方面。
-
STL 具有高可重用性,高性能,高移植性,跨平臺的優點。
STL三大組件
容器
常用的數據結構無外乎數組(array),鏈表(list),tree(樹),棧(stack),隊列(queue),集合(set),映射表(map),根據數據在容器中的排列特性,這些數據分為序列式容器和關聯式容器兩種。
Ø 序列式容器
就是容器元素在容器中的位置是由元素進入容器的時間和地點來決定。Vector容器、Deque容器、List容器、Stack容器、Queue容器。
Ø 關聯式容器
是指容器已經有了一定的規則,容器元素在容器中的位置由我的規則來決定。Set/multiset容器 Map/multimap容器
演算法
演算法分為:質變演算法和非質變演算法。
質變演算法:是指運算過程中會更改區間內的元素的內容。例如拷貝,替換,刪除等等
非質變演算法:是指運算過程中不會更改區間內的元素內容,例如查找、計數、遍歷、尋找極值等等
迭代器
迭代器(iterator)是一種抽象的設計概念,現實程式語言中並沒有直接對應於這個概念的實物。在<
迭代器的設計思維-STL的關鍵所在,STL的中心思想在於將數據容器(container)和演算法(algorithms)分開,彼此獨立設計,最後再一貼膠著劑將他們撮合在一起。從技術角度來看,容器和演算法的泛型化並不困難,c++的class template和function template可分別達到目標,如果設計出兩這個之間的良好的膠著劑,才是大難題。
迭代器的種類:
輸入迭代器 | 提供對數據的只讀訪問 | 只讀,支持++、==、!= |
---|---|---|
輸出迭代器 | 提供對數據的只寫訪問 | 只寫,支持++ |
前向迭代器 | 提供讀寫操作,並能向前推進迭代器 | 讀寫,支持++、==、!= |
雙向迭代器 | 提供讀寫操作,並能向前和向後操作 | 讀寫,支持++、--, |
隨機訪問迭代器 | 提供讀寫操作,並能在數據中隨機移動 | 讀寫,支持++、--、[n]、-n、<、<=、>、>= |
void test2() {
vector<int>r;//STL 中的標準容器之一 :動態數組
r.push_back(1);//vector 容器提供的插入數據的方法
r.push_back(2);
r.push_back(7);
//vector 容器提供了 begin()方法 返回指向第一個元素的迭代器
//vector 容器提供了 end()方法 返回指向最後一個元素下一個位置的迭代器
vector<int>::iterator reg = r.begin();
vector<int>::iterator ina = r.end();//這是一種隨機訪問類型的迭代器
while (reg != ina) {
cout << *reg << " ";
reg++;
}
cout << endl;
//演算法 count 演算法 用於統計元素的個數
int total = count(reg, ina, 5);
cout << "total:" << total << endl;
}
STL 容器不單單可以存儲基礎數據類型,也可以存儲類對象
class Regina {
public:
Regina(int a) : age(a) {};
int age;
~Regina(){};
};
vector<Regina>r;
Regina r1(1), r2(2), r3(7);
r.push_back(r1);
r.push_back(r2);
r.push_back(r3);
vector<Regina>::iterator pStart = r.begin();
vector<Regina>::iterator pEnd = r.end();
while (pStart != pEnd) {
cout << "pStart->age: " << pStart->age << " ";
pStart++;
}
cout << endl;
還可以存儲類的指針
在 C++ 中,當我們使用迭代器遍歷容器時,迭代器本身是一個指向容器中元素的對象,而不是實際的元素本身。因此,為了訪問容器中存儲的元素,我們需要通過解引用操作符 *
來獲取迭代器指向的實隕對象.具體的vector怎麼用在下一篇
vector<Regina*> v;//存儲 Teacher 類型指針
Regina* r1 = new Regina(1);
Regina* r2 = new Regina(2);
Regina* r3 = new Regina(7);
v.push_back(r1);
v.push_back(r2);
v.push_back(r3);
//拿到容器迭代器
vector<Regina*>::iterator pStart = v.begin();
vector<Regina*>::iterator pEnd = v.end();
//通過迭代器遍歷
while (pStart != pEnd) {
cout << (*pStart)->age << " ";
pStart++;
}
cout << endl;
常用容器講解
string
C風格字元串(以空字元結尾的字元數組)太過複雜難於掌握,不適合大程式的開發,所以C++標準庫定義了一種string類,定義在頭文件
String和c風格字元串對比:
-
Char是一個指針,String是一個類
string封裝了char,管理這個字元串,是一個char型的容器。
-
String封裝了很多實用的成員方法
查找find,拷貝copy,刪除delete 替換replace,插入insert
-
不用考慮記憶體釋放和越界
string管理char*所分配的記憶體。每一次string的複製,取值都由string類負責維護,不用擔心複製越界和取值越界等。
string regina1("regina");
string regina2(7, '$');
string regina3(regina1);
cout << regina1 << endl; // 輸出 "regina"
cout << regina2 << endl; // 輸出 "$$$$$$$"
cout << regina3 << endl; // 輸出 "regina"
regina1 += "baby";
cout << regina1 << endl; // 輸出 "reginababy"
string regina4 = regina1 + regina3;
char arr[] = "arr loop";
string regina5(arr, 2);
string regina6(arr + 1, arr + 3);
cout << regina1 << endl; // 輸出 "reginababy"
cout << regina4 << endl; // 輸出 "reginababyregina"
cout << regina5 << endl; // 輸出 "ar"
cout << regina6 << endl; // 輸出 "rr"
string regina7(®ina4[1], ®ina4[4]);
string regina8(regina4, 1, 5);
cout << regina7 << endl; // 輸出 "egi"
cout << regina8 << endl; // 輸出 "egina"
基本賦值操作
string& operator=(const char* s):將 char* 類型的字元串賦值給當前的字元串。
string& operator=(const string& s):將另一個字元串對象 s 賦值給當前的字元串。
string& operator=(char c):將單個字元 c 賦值給當前的字元串。
string& assign(const char* s):將 char* 類型的字元串 s 賦值給當前的字元串。
string& assign(const char* s, int n):將 char* 類型的字元串 s 的前 n 個字元賦值給當前的字元串。
string& assign(const string& s):將另一個字元串對象 s 賦值給當前的字元串。
string& assign(int n, char c):使用 n 個字元 c 來賦值給當前的字元串。
string& assign(const string& s, int start, int n):將字元串 s 從位置 start 開始的 n 個字元賦值給當前的字元串。
string s1, s2, s3;
// 使用賦值操作符重載將 char* 類型的字元串賦值給當前的字元串
s1 = "regina, baby!";
cout << "s1: " << s1 << endl;
// 使用賦值操作符重載將另一個字元串對象賦值給當前的字元串
s2 = s1;
cout << "s2: " << s2 << endl;
// 使用 assign() 成員函數將 char* 類型的字元串賦值給當前的字元串
s3.assign("regina", 4);
cout << "s3: " << s3 << endl;
// 使用 assign() 成員函數將另一個字元串對象賦值給當前的字元串
s1.assign(s3);
cout << "s1: " << s1 << endl;
// 使用 assign() 成員函數使用 n 個字元 c 賦值給當前的字元串
s2.assign(8, '*');
cout << "s2: " << s2 << endl;
// 使用 assign() 成員函數將字元串 s 的子串賦值給當前的字元串
string s4 = "regina, baby!";
string s5;
s5.assign(s4, 7, 5); // 從索引 7 開始的 5 個字元
cout << "s5: " << s5 << endl;
存取字元串
char& operator[](int n)
:通過重載[]
運算符來獲取字元串中索引為n
的字元。這種方式不進行邊界檢查,如果訪問超出字元串範圍的位置,可能導致未定義行為。char& at(int n)
:通過at
方法來獲取字元串中索引為n
的字元。與使用operator[]
不同的是,at
方法會進行邊界檢查,如果訪問超出字元串範圍的位置,會拋出out_of_range
異常。
string r = "regina";
// 使用 operator[] 獲取字元
cout << "Character at index 7 (using operator[]): " << r[5] << endl;
// 使用 at 方法獲取字元
try {
cout << "Character at index 3 (using at): " << r.at(3) << endl;
cout << "Character at index 13 (using at): " << r.at(13) << endl;
}
catch (const out_of_range& e) {
cerr << "Exception caught: " << e.what() << endl;
}
cerr
是 C++ 中的一個輸出流對象,它與標準錯誤流 (stderr
) 相關聯。與標準輸出流 (cout
) 不同,標準錯誤流用於輸出程式中的錯誤消息和診斷信息。
cerr
對象通常會直接將消息輸出到終端或日誌文件,而不會進行緩衝操作。這意味著錯誤消息會立即顯示在終端上,而不需要等待緩衝區刷新。因此,cerr
在處理錯誤和調試信息時非常有用。
拼接字元串
string& operator+=(const string& str), string& operator+=(const char* str), string& operator+=(const char c):這些是重載 += 操作符的方法,用於將另一個字元串、C風格字元串或字元連接到當前字元串的末尾。
string& append(const char *s):將 C 風格的字元串 s 追加到當前字元串的末尾。
string& append(const char *s, int n):將 C 風格的字元串 s 的前 n 個字元追加到當前字元串的末尾。
string& append(const string& s):將字元串 s 追加到當前字元串的末尾,與 operator+= 的功能類似。
string& append(const string& s, int pos, int n):將字元串 s 中從索引 pos 開始的 n 個字元追加到當前字元串的末尾。
string& append(int n, char c):在當前字元串的末尾添加 n 個字元 c。
string str = "Hello";
// 使用 += 操作符連接字元串
str += ", world!";
cout << "After +=: " << str << endl;
// 使用 append(const char*) 連接字元串
str.append(" This is a test.");
cout << "After append(const char*): " << str << endl;
// 使用 append(const string&) 連接字元串
string additional = " Have a nice day!";
str.append(additional);
cout << "After append(const string&): " << str << endl;
// 使用 append(const char*, int) 連接部分字元串
str.append(" Testing", 4); // 添加 " Test"
cout << "After append(const char*, int): " << str << endl;
// 使用 append(int, char) 添加字元
str.append(3, '!'); // 添加 "!!!"
cout << "After append(int, char): " << str << endl;
查找和替換
string words = "regina baby is my sM baby";
int pos = words.find("baby");
cout << pos << '\n';
pos = words.find("sM", 4, 2);
cout << pos << '\n';
pos = words.rfind("baby");
cout << pos << '\n';
比較操作
/*
compare函數在>時返回 1,<時返回 -1,==時返回 0。
比較區分大小寫,比較時參考字典順序,排越前面的越小。
大寫的A比小寫的a小。
*/
int compare(const string& s) const;//與字元串s比較
int compare(const char *s) const;//與字元串s比較
string s1 = "regina";
string s2 = "ivanlee";
cout << "s1.compare(s2): " << s1.compare(s2) << "\n";
const char* cs2 = "leeivan";
cout << "s2.compare(cs2): " << s2.compare(cs2) << "\n";
插入和刪除
string& insert(int pos, constchar* s); //插入字元串
string& insert(int pos, conststring& str); //插入字元串
string& insert(int pos, int n, char c);//在指定位置插入n個字元c
string& erase(int pos, int n = npos);//刪除從Pos開始的n個字元
string s = "hello regina";
s.insert(5, "beautiful");
cout << s << "\n";
s.insert(s.length(), 2, '!'); //單引號
cout << s << "\n";
s.erase(5, 3);
cout << s << "\n";
string和c-style字元串轉換
string s = "regina";
const char* c = s.c_str();//返回的是一個指向常量字元的指針
cout << "s.c_str(): " << typeid(c).name() << endl;
const char* c1 = "regina";
string s1(s);
cout << "string s1(s); " << typeid(s1).name() << endl;
在c++中存在一個從const char*到string的隱式類型轉換,卻不存在從一個string對象到C_string的自動類型轉換。對於string類型的字元串,可以通過c_str()函數返回string對象對應的C_string.
通常,程式員在整個程式中應堅持使用string類對象,直到必須將內容轉化為char*時才將其轉換為C_string.
為了修改string字元串的內容,下標操作符[]和at都會返回字元的引用。但當字元串的記憶體被重新分配之後,可能發生錯誤.
自動擴容
在 C++ 的標準庫中,std::string
類具有自動調整大小的功能。當你向 std::string
對象添加字元時,如果當前容量不足以存儲新的字元,std::string
會自動重新分配記憶體以擴展容量,從而確保可以存儲新的字元並保持字元串的有效性。
這種自動調整大小的過程是由 std::string
類內部管理的,開發者無需手動管理字元串的記憶體大小。當添加字元導致字元串超出當前容量時,std::string
會選擇一個新的更大的記憶體塊,將舊數據複製到新的記憶體塊中,並釋放舊的記憶體塊。
這種自動調整大小的機制使得 std::string
更加方便和易用,開發者可以專註於操作字元串內容而不必擔心記憶體管理的細節。
std::string str = "regina";
std::cout << "Initial capacity: " << str.capacity() << std::endl;
// 添加字元,觀察容量的變化
for (char c = 'a'; c <= 'z'; ++c) {
str.push_back(c);
std::cout << "Capacity after adding '" << c << "': " << str.capacity() << std::endl;
}
std::string
類提供了 capacity()
方法,用於返回當前字元串對象的容量大小。容量表示為當前存儲空間的大小,它可能大於等於字元串的長度,因為 std::string
可能會分配比實際字元串長度更多的記憶體,以便進行後續的添加操作而不需要頻繁地重新分配記憶體。
通常情況下,當你向 std::string
對象添加字元時,如果當前的容量不足以存儲新的字元,std::string
會自動重新分配記憶體以擴展容量,從而確保可以存儲新的字元並保持字元串的有效性。這樣的設計可以提高性能,因為它避免了頻繁的記憶體分配和釋放操作。
本文來自博客園,作者:ivanlee717,轉載請註明原文鏈接:https://www.cnblogs.com/ivanlee717/p/18057918