摘要:本文深入探討 C++ 提高編程中的模板編程與標準模板庫(STL)相關內容。詳細闡述模板編程中函數模板和類模板的概念、語法、特性及應用案例;對 STL 的誕生背景、基本概念、六大組件進行剖析,并對常用容器、函數對象、常用算法展開全面講解,旨在為 C++ 開發者提供系統且深入的技術參考,助力其提升 C++ 編程能力與代碼質量。
一、引言
????????C++ 作為一種強大且應用廣泛的編程語言,其提高編程部分涵蓋的模板編程和 STL,為開發者提供了高度的代碼復用性和強大的數據處理能力。模板編程實現了泛型編程,使代碼能夠獨立于具體數據類型;STL 則是一套精心設計的模板庫,包含容器、算法和迭代器等組件,極大地簡化了復雜數據結構和算法的實現。深入理解和掌握這些內容,對提升 C++ 編程水平至關重要。
二、模板編程
(一)模板概念
????????模板是 C++ 泛型編程的基礎,它允許將類型作為參數傳遞給函數或類,從而實現代碼的復用。通過模板,算法可以獨立于具體的數據類型,提高了代碼的通用性和可維護性。其核心基于類型參數化,使得同一套代碼能夠處理不同類型的數據,減少了重復代碼的編寫。
(二)函數模板
- 語法
函數模板以template <typename T>
或template <class T>
的形式聲明模板參數,其中typename
和class
含義相同,用于指定類型參數。在函數定義中,使用該模板參數來表示函數的參數類型或返回值類型。例如:
template <typename T>
T add(T a, T b) {return a + b;
}
- 注意事項
類型推導存在一定規則,編譯器優先匹配精確類型,當無法自動推導模板參數類型時,需要開發者顯式指定。同時,在模板參數匹配時,普通函數的優先級高于函數模板。 - 案例
實現一個通用的數組元素交換函數:
template <typename T>
void swapArray(T arr[], int i, int j) {T temp = arr[i];arr[i] = arr[j];arr[j] = temp;
}
????????此函數可用于交換不同類型數組中的元素,展示了函數模板的便捷性。再如編寫通用的最大值獲取函數:
template <typename T>
T getMax(T a, T b) {return (a > b)? a : b;
}
????????體現了函數模板對不同數據類型的適用性。
4.?與普通函數區別
????????函數模板具有類型通用性,能夠處理多種數據類型;而普通函數針對特定類型編寫。在編譯時,函數模板會根據實際傳入的參數類型進行實例化,生成對應類型的函數代碼,普通函數則直接進行編譯。
5.?調用規則
????????自動類型推導:編譯器根據傳入函數的參數自動確定模板參數的類型,如int result = add(3, 5);
。顯式指定類型:開發者明確指定模板參數類型,例如add<int>(3, 5)
。
6.?局限性
????????對于一些特定類型的操作,如位運算等,函數模板可能需要特殊處理。此外,大量實例化可能導致代碼體積膨脹,占用更多的存儲空間。
(三)類模板
- 語法
類模板的聲明形式為template <typename T> class ClassName {...}
?,在類內部可以使用模板參數T
定義成員變量和成員函數。成員函數既可以在類內直接定義,也可以在類外實現。類外實現成員函數時,語法如下:
template <typename T>
return_type ClassName<T>::function_name(parameters) {...}
- 與函數模板區別
類模板作用于整個類及其對象實例,而函數模板僅作用于函數。類模板可包含多種成員,如數據成員、成員函數等,結構更為復雜,涉及到類的封裝、繼承和多態等特性與模板的結合。 - 成員函數創建時機
類模板的成員函數在調用時才會進行實例化,生成對應類型的函數代碼。提前聲明成員函數并不立即實例化,這種延遲實例化機制可以節省編譯時間,只有在實際使用時才進行實例化操作。 - 對象做函數參數
按值傳遞:將對象復制一份傳遞給函數,這種方式開銷較大,適用于小對象或無需保留原對象狀態的情況。例如:
template <typename T>
void printObjectByValue(T obj) {std::cout << obj << std::endl;
}
????????引用傳遞:傳遞對象的引用給函數,高效且能修改對象內容,同時避免了拷貝構造的開銷。如:
template <typename T>
void modifyObjectByReference(T& obj) {// 修改obj操作
}
????????指針傳遞:通過指針傳遞對象,具有較高的靈活性,可實現多態等特性,但需要注意指針的有效性。示例如下:
template <typename T>
void operateOnObjectByPointer(T* ptr) {// 通過指針操作對象
}
- 與繼承
類模板繼承普通類:子類模板可以繼承普通類的屬性和方法,并在此基礎上添加模板特性。例如,普通類實現了一些通用的功能,類模板繼承后可以針對不同類型進行定制化擴展。
類模板之間繼承:子類模板可以繼承父類模板的參數及功能,并且可以進一步擴展新的模板參數或成員。在繼承過程中,需要注意模板參數的傳遞和處理,確保繼承關系的正確性和代碼的可讀性。 - 成員函數類外實現
類模板成員函數類外實現時,需遵循特定語法,明確指定模板參數。同時要注意作用域的限定,確保編譯器能夠正確識別和編譯。例如:
template <typename T>
class MyClass {
public:void function();
};template <typename T>
void MyClass<T>::function() {// 函數實現代碼
}
- 分文件編寫
頭文件:主要用于聲明類模板及其成員函數,為其他文件提供接口。在頭文件中,應包含必要的模板聲明和類的前置聲明,確保其他源文件能夠正確引用。
源文件:實現類模板的成員函數。在編譯時,需要正確處理模板實例化問題,常見的解決方法包括顯式實例化或在源文件中包含頭文件等方式,以避免編譯鏈接錯誤。 - 與友元
普通函數友元:授予普通函數訪問類模板私有成員的權限,通過在類模板中聲明友元函數來實現。例如:
template <typename T>
class MyClass {friend void friendFunction(MyClass<T>& obj);
private:T data;
};template <typename T>
void friendFunction(MyClass<T>& obj) {// 訪問obj的私有成員data
}
????????函數模板友元:函數模板可以作為類模板的友元,實現特定功能的交互。在聲明和實現時,需要注意模板參數的匹配和作用域的處理。
????????類模板友元:一個類模板可以作為另一個類模板的友元,從而實現兩個類模板之間的數據共享或功能協作。在這種情況下,需要仔細設計友元關系,確保代碼的安全性和可維護性。
9.?案例
????????實現通用的鏈表類模板:
template <typename T>
class LinkedList {
private:struct Node {T data;Node* next;Node(const T& value) : data(value), next(nullptr) {}};Node* head;
public:LinkedList() : head(nullptr) {}// 鏈表操作函數,如插入、刪除、遍歷等
};
????????該類模板可用于管理不同類型數據的鏈表節點,實現鏈表的基本操作。再如構建通用的矩陣類模板:
template <typename T>
class Matrix {
private:int rows;int cols;T** elements;
public:Matrix(int r, int c) : rows(r), cols(c) {elements = new T*[rows];for (int i = 0; i < rows; ++i) {elements[i] = new T[cols];}}// 矩陣運算函數,如加法、乘法等
};
????????實現矩陣的存儲和運算功能,展示類模板在復雜數據結構中的應用。
三、STL 初識
(一)STL 誕生
????????STL 起源于惠普實驗室,由 Alexander Stepanov 等人開發。其設計目標是提供一套通用、高效、可復用的模板庫,旨在簡化 C++ 編程中數據結構和算法的實現,減少開發者的重復勞動,提高軟件開發的效率和質量。
(二)基本概念
- 容器:用于存儲數據,是 STL 的重要組成部分。類似于不同的數據結構,如數組、鏈表、集合等,提供了不同的存儲和訪問方式,滿足各種應用場景對數據存儲的需求。
- 算法:對容器中的數據進行操作,涵蓋排序、查找、遍歷等多種操作。STL 算法具有高度的通用性,能夠處理不同類型的容器數據,通過迭代器與容器進行交互。
- 迭代器:類似于指針,用于訪問容器中的元素,為容器提供了統一的訪問接口。迭代器的存在使得算法可以不依賴于具體容器的實現細節,實現了容器和算法的分離與通用化。
(三)六大組件
- 容器
順序容器:如vector
、list
、deque
等,它們按照元素的插入順序存儲元素。vector
是動態數組,具有連續的內存存儲,支持隨機訪問,適合頻繁訪問元素的場景;list
是雙向鏈表,不支持隨機訪問,但在頻繁插入和刪除元素時效率較高;deque
是雙端隊列,允許在兩端進行高效的插入和刪除操作,兼具vector
和list
的部分特性。
關聯容器:包括set
、map
等,這些容器根據特定的鍵值關系存儲元素。set
中的元素唯一且自動排序,常用于快速查找和去重;map
以鍵值對的形式存儲數據,鍵唯一且自動排序,可用于高效的鍵值查找。 - 算法
分為多種類型,如遍歷算法用于逐個訪問容器中的元素;查找算法用于在容器中尋找特定元素或滿足條件的元素;排序算法用于對容器中的元素進行排序等。不同的算法針對不同的應用需求,提供了豐富的數據處理功能。 - 迭代器
輸入迭代器:只讀,支持單遍掃描容器,用于從容器中讀取數據。
輸出迭代器:只寫,用于向容器中寫入數據。
前向迭代器:支持讀寫操作,只能單遍掃描容器,按順序向前訪問元素。
雙向迭代器:支持讀寫操作,可在容器中前后移動,適用于需要雙向遍歷的場景。
隨機訪問迭代器:支持讀寫操作,并且可以隨機訪問容器中的元素,具有類似指針算術運算的能力,如vector
的迭代器。 - 仿函數
即函數對象,通過類重載()
運算符實現。仿函數可以作為算法的參數,用于定制算法的操作邏輯。例如,在排序算法中,可以使用自定義的仿函數作為比較函數,實現特定的排序規則。 - 適配器
容器適配器:如stack
、queue
等,它們改變了其他容器的接口,使其符合特定的數據結構特性。stack
遵循后進先出原則,queue
遵循先進先出原則,它們底層通常基于其他容器(如deque
、vector
等)實現。
迭代器適配器:如reverse_iterator
,它改變了迭代器的遍歷方向,使算法可以反向遍歷容器。 - 分配器
負責容器的內存分配與釋放,默認情況下使用系統的內存分配機制。開發者也可以自定義分配器,以滿足特定的內存管理需求,如提高內存分配效率、實現內存池等功能。
(四)容器、算法、迭代器
????????迭代器在容器和算法之間起到橋梁作用。算法通過迭代器來訪問和操作容器中的元素,使得算法能夠獨立于具體容器的實現。容器提供了數據的存儲結構,而算法基于迭代器對容器中的數據進行處理,這種設計模式實現了容器和算法的分離與高度復用,提高了代碼的可維護性和擴展性。
(五)容器算法迭代器初識
- vector 存放內置數據類型
語法:定義vector<int> v;
創建一個存儲int
類型數據的vector
容器。插入元素使用v.push_back(5);
,訪問元素可以通過v[0]
或v.at(0)
(at
函數帶有邊界檢查)。
動態內存管理:vector
會根據元素的插入自動擴展內存,采用連續內存存儲方式,這使得元素訪問效率較高,同時也支持隨機訪問。 - vector 存放自定義數據類型
適配:自定義數據類型需要實現合適的構造函數、拷貝構造函數、賦值運算符等,以滿足vector
對元素的存儲和操作要求。例如,如果自定義類沒有正確實現拷貝構造函數,在vector
進行元素拷貝(如插入、刪除等操作可能涉及拷貝)時可能會出現錯誤。
操作:可以對存放自定義數據類型的vector
進行排序、查找等操作,但通常需要定制比較函數等,以確保操作符合自定義類型的邏輯。 - vector 容器嵌套容器
應用場景:常用于表示二維數據結構,如二維數組表示矩陣、表格等。例如,vector<vector<int>> matrix;
可以表示一個整數矩陣。
內存布局與訪問:外層vector
管理內層vector
的指針,內存并非完全連續。訪問元素時需要通過兩層索引,如matrix[i][j]
來訪問第i
行第j
列的元素。
四、STL - 常用容器
(一)string 容器
- 基本概念
string
容器用于動態存儲字符串,它自動管理內存,相比 C 風格字符串更加安全和易用。其內部采用類似vector
的結構來存儲字符數據,提供了豐富的操作接口來處理字符串。 - 構造函數
默認構造:string s;
創建一個空字符串。
拷貝構造:string s1(s);
將已有字符串s
復制給s1
。
初始化列表構造:string s2("hello");
使用字符串字面量初始化s2
。 - 賦值操作
重載=
運算符:s = "world";
直接將字符串字面量賦值給s
。
assign 函數:提供多種賦值方式,如s.assign("new string", 3);
表示取"new string"
的前 3 個字符賦值給s
。 - 字符串拼接
重載+
運算符:s = s + "!";
將"!"
拼接到字符串s
后面。
append 函數:s.append(" append");
可指定位置和長度進行字符串拼接,如s.append(" append", 3);
表示拼接" app"
。 - 查找和替換
find 函數:size_t pos = s.find("ll");
用于查找子串"ll"
在字符串s
中的位置。
replace 函數:s.replace(pos, 2, "XX");
將從位置pos
開始長度為 2 的子串替換為"XX"
。 - 字符串比較
重載比較運算符:如if (s1 == s2)
通過重載的==
運算符按字典序比較兩個字符串。
比較規則:從左到右逐個字符比較 ASCII 碼值,若所有字符都相同則字符串相等,否則根據第一個不同字符的 ASCII 碼大小確定字符串的大小關系。 - 字符存取
重載[]
運算符:char c = s[0];
直接訪問字符串s
的第一個字符。
at 函數:char c1 = s.at(0);
與[]
類似,但at
函數帶有越界檢查,當訪問越界時會拋出異常。 - 插入和刪除
insert 函數:s.insert(1, "x");
在字符串s
的索引位置 1 處插入字符"x"
。
erase 函數:s.erase(1, 2);
刪除字符串s
從索引位置 1 開始長度為 2 的字符。 - 子串
substr 函數:string sub = s.substr(1, 3);
獲取字符串s
從索引位置 1 開始長度為 3 的子串。
應用場景:常用于文本解析、字符串截取等操作,例如從一個完整的文件名中截取文件擴展名。
(二)vector 容器
????????resize 和 reserve 函數:resize
函數用于改變vector
中元素的個數,可指定初始值,例如v.resize(10, 0)
將vector
的大小調整為 10,新添加的元素初始值為 0;reserve
函數用于調整vector
的容量,不改變元素個數,它可以提前分配內存,減少內存重新分配的開銷,如v.reserve(20)
表示為v
預留能容納 20 個元素的內存空間。
5.?插入和刪除
????????push_back 函數:在vector
的尾部插入元素,v.push_back(6);
將元素 6 添加到vector
的末尾。
????????insert 函數:可以在指定位置插入元素,v.insert(v.begin() + 1, 7);
表示在vector
的第二個位置(索引為 1)插入元素 7。
????????pop_back 函數:刪除vector
尾部的元素,v.pop_back();
移除vector
的最后一個元素。
????????erase 函數:刪除指定位置或范圍的元素,v.erase(v.begin() + 2);
刪除vector
中索引為 2 的元素;v.erase(v.begin() + 1, v.begin() + 3);
則刪除從索引 1 到索引 2(不包括索引 3)的元素。
6.?數據存取
????????重載[]
運算符:通過v[0]
可以直接訪問vector
中索引為 0 的元素。
????????at 函數:v.at(0)
與[]
運算符功能類似,但at
函數會進行越界檢查,若索引越界會拋出異常,而[]
運算符在越界時行為未定義。
7.?互換容器
????????swap 函數:swap(v, v1);
用于交換兩個vector
容器的內容。這在某些場景下可以用于內存優化,例如當需要快速交換兩個vector
的元素時,使用swap
函數比逐個元素交換效率更高。
8.?預留空間
????????reserve 函數:如前面所述,reserve
函數可以提前為vector
分配內存,避免在插入元素時頻繁進行內存重新分配。例如,當已知需要向vector
中插入大量元素時,提前調用reserve
函數可以顯著提高程序性能。
(三)deque 容器
- 基本概念
deque
(雙端隊列)允許在隊列的兩端進行插入和刪除操作,與vector
相比,它在頭部和尾部的操作更加高效。其內存結構是分段連續的,通過映射表來管理各個內存塊,這使得它在需要頻繁在兩端操作元素的場景中表現出色。 - 構造函數
默認構造:deque<int> d;
創建一個空的deque
容器。
帶參數構造:deque<int> d1(5, 8);
創建一個包含 5 個元素,每個元素值為 8 的deque
。 - 賦值操作
重載=
運算符:d = d1;
將d1
的內容復制給d
。
assign 函數:d.assign(4, 9);
用 4 個值為 9 的元素替換d
原來的內容。 - 大小操作
size 函數:用于獲取deque
中元素的個數,int s = d.size();
。
empty 函數:判斷deque
是否為空,bool isEmpty = d.empty();
。 - 插入和刪除
push_front 函數:在deque
的頭部插入元素,d.push_front(1);
將元素 1 添加到deque
的開頭。
push_back 函數:在deque
的尾部插入元素,d.push_back(2);
將元素 2 添加到deque
的末尾。
pop_front 函數:刪除deque
頭部的元素,d.pop_front();
移除deque
的第一個元素。
pop_back 函數:刪除deque
尾部的元素,d.pop_back();
移除deque
的最后一個元素。
insert 函數:可以在指定位置插入元素,d.insert(d.begin() + 1, 3);
在deque
的第二個位置(索引為 1)插入元素 3。
erase 函數:刪除指定位置或范圍的元素,d.erase(d.begin() + 2);
刪除deque
中索引為 2 的元素;d.erase(d.begin() + 1, d.begin() + 3);
刪除從索引 1 到索引 2(不包括索引 3)的元素。 - 數據存取
重載[]
運算符:通過d[0]
可以直接訪問deque
中索引為 0 的元素。
at 函數:d.at(0)
與[]
運算符功能類似,但at
函數會進行越界檢查,若索引越界會拋出異常,而[]
運算符在越界時行為未定義。 - 排序
deque
可以使用sort
算法進行排序,例如sort(d.begin(), d.end());
。如果需要按照特定的規則進行排序,可以自定義比較函數,將其作為sort
算法的參數,實現對deque
中元素的定制化排序。
(四)案例 - 評委打分
- 案例描述
該案例模擬比賽評分的場景,在比賽中多位評委為選手打分,為了保證評分的公正性,需要去除一個最高分和一個最低分,然后計算剩余分數的平均值,以此作為選手的最終得分。此案例涉及到數據的存儲、處理和結果的輸出等環節,通過使用 STL 容器和算法可以高效地實現。 - 實現步驟
數據錄入:使用合適的容器(如vector
)來存儲評委的分數。例如,vector<double> scores;
然后通過循環等方式將評委的分數依次錄入到容器中,如scores.push_back(score);
(其中score
為從輸入獲取的評委分數)。
數據處理:首先查找容器中的最高分和最低分,可以使用max_element
和min_element
算法,如auto maxIt = max_element(scores.begin(), scores.end());
和auto minIt = min_element(scores.begin(), scores.end());
。然后從容器中刪除最高分和最低分,scores.erase(maxIt);
和scores.erase(minIt);
。最后計算剩余分數的平均值,通過累加剩余分數并除以剩余分數的個數來實現,如double sum = accumulate(scores.begin(), scores.end(), 0.0); double average = sum / scores.size();
。
結果輸出:將計算得到的選手最終得分輸出顯示,如cout << "選手的最終得分是: " << average << endl;
。
(五)stack 容器
- 基本概念
??stack
是一種遵循后進先出(LIFO)原則的數據結構。在實際應用中,常用于函數調用棧的模擬、表達式求值(如后綴表達式計算)等場景。它底層通常基于其他容器(如deque
、vector
)實現,通過對這些容器的接口進行封裝,使其符合棧的特性。 - 常用接口
push 函數:用于將元素壓入棧頂,s.push(5);
將元素 5 添加到棧s
的頂部。
pop 函數:用于移除棧頂的元素,s.pop();
刪除棧s
的棧頂元素。
top 函數:用于獲取棧頂的元素,但不刪除該元素,int topVal = s.top();
獲取棧s
的棧頂元素值。
size 函數:用于獲取棧中元素的個數,int stackSize = s.size();
。
empty 函數:用于判斷棧是否為空,bool isStackEmpty = s.empty();
。
(六)queue 容器
- 基本概念
??queue
是一種遵循先進先出(FIFO)原則的數據結構。在實際應用中,常用于消息隊列、任務隊列等場景,例如在多線程編程中,任務可以按照先進先出的順序放入任務隊列,然后由線程依次取出執行。它底層也通常基于其他容器(如deque
、list
)實現,通過封裝容器接口來滿足隊列的特性。 - 常用接口
push 函數:用于將元素插入隊尾,q.push(3);
將元素 3 添加到隊列q
的末尾。
pop 函數:用于移除隊頭的元素,q.pop();
刪除隊列q
的隊頭元素。
front 函數:用于獲取隊頭的元素,但不刪除該元素,int frontVal = q.front();
獲取隊列q
的隊頭元素值。
back 函數:用于獲取隊尾的元素,但不刪除該元素,int backVal = q.back();
獲取隊列q
的隊尾元素值。
size 函數:用于獲取隊列中元素的個數,int queueSize = q.size();
。
empty 函數:用于判斷隊列是否為空,bool isQueueEmpty = q.empty();
。
(七)list 容器
- 基本概念
??list
是一個雙向鏈表,它的每個節點都包含一個指向前驅節點和一個指向后繼節點的指針。由于其內存不是連續的,因此不支持隨機訪問,但是在頻繁進行插入和刪除操作時具有較高的效率。適用于需要頻繁插入和刪除元素,而對隨機訪問需求較少的場景,如實現一個動態的任務列表,任務可以隨時添加或刪除。 - 構造函數
默認構造:list<int> l;
創建一個空的list
容器。
帶參數構造:list<int> l1(4, 7);
創建一個包含 4 個元素,每個元素值為 7 的list
。 - 賦值操作
重載=
運算符:l = l1;
將l1
的內容復制給l
。
assign 函數:l.assign(3, 8);
用 3 個值為 8 的元素替換l
原來的內容。 - 插入和刪除
push_front 函數:在list
的頭部插入元素,l.push_front(1);
將元素 1 添加到list
的開頭。
push_back 函數:在list
的尾部插入元素,l.push_back(2);
將元素 2 添加到list
的末尾。
pop_front 函數:刪除list
頭部的元素,l.pop_front();
移除list
的第一個元素。
pop_back 函數:刪除list
尾部的元素,l.pop_back();
移除list
的最后一個元素。
insert 函數:可以在指定位置插入元素,l.insert(l.begin(), 3);
在list
的開頭插入元素 3。
erase 函數:刪除指定位置或范圍的元素,l.erase(l.begin() + 1);
刪除list
中索引為 1 的元素;l.erase(l.begin() + 1, l.begin() + 3);
刪除從索引 1 到索引 2(不包括索引 3)的元素。 - 數據存取
由于list
不支持隨機訪問,因此不能像vector
那樣通過下標直接訪問元素。需要通過迭代器來遍歷list
,例如:
for (auto it = l.begin(); it != l.end(); ++it) {int val = *it;// 對val進行操作
}
- 排序
list
可以使用sort
函數進行排序,l.sort();
。同樣,如果需要按照特定的規則進行排序,可以自定義比較函數,將其作為sort
函數的參數,實現對list
中元素的定制化排序。
(八)set/multiset 容器
- 基本概念
set
是一個集合容器,其中的元素唯一且自動排序,底層通常使用紅黑樹實現。這種數據結構使得在set
中查找元素的效率較高,適用于需要快速查找和去重的場景,例如統計一篇文章中不重復的單詞。
multiset
與set
類似,也是基于紅黑樹實現,但其允許元素重復,適用于需要統計元素出現次數等場景,比如統計一個班級學生成績中各個分數的出現次數。 - 構造和賦值
默認構造:set<int> s;
和multiset<int> ms;
分別創建一個空的set
和multiset
容器。
拷貝構造:set<int> s1(s);
和multiset<int> ms1(ms);
分別將已有的set
和multiset
容器進行復制。
賦值運算符重載:s = s1;
和ms = ms1;
分別實現set
和multiset
容器之間的賦值操作。 - 大小和交換
size 函數:用于獲取set
或multiset
中元素的個數,int setSize = s.size();
和int multisetSize = ms.size();
。
empty 函數:用于判斷set
或multiset
是否為空,bool isSetEmpty = s.empty();
和bool isMultisetEmpty = ms.empty();
。
swap 函數:swap(s, s1);
和swap(ms, ms1);
分別用于交換兩個set
或兩個multiset
容器的內容。 - 插入和刪除
insert 函數:用于向set
或multiset
中插入元素,s.insert(5);
和ms.insert(5);
分別向set
和multiset
中插入元素 5。在set
中,如果插入的元素已經存在,則插入操作不會生效;而在multiset
中,相同元素可以多次插入。
erase 函數:用于刪除指定的元素或范圍,s.erase(s.find(5));
和ms.erase(ms.find(5));
分別刪除set
和multiset
中值為 5 的元素(在multiset
中會刪除所有值為 5 的元素);s.erase(s.begin(), s.end());
和ms.erase(ms.begin(), ms.end());
分別刪除set
和multiset
中的所有元素。 - 查找和統計
find 函數:用于查找元素,set<int>::iterator it = s.find(5);
和multiset<int>::iterator mit = ms.find(5);
分別在set
和multiset
中查找值為 5 的元素,并返回指向該元素的迭代器(如果元素不存在,則返回end()
迭代器)。
count 函數:在multiset
中用于統計元素出現的次數,int countVal = ms.count(5);
統計multiset
中值為 5 的元素的個數;在set
中,count
函數的返回值只能是 0 或 1,因為set
中元素唯一。 - set 和 multiset 區別
元素唯一性:set
中的元素是唯一的,不允許重復;而multiset
允許元素重復出現。
應用場景:set
主要用于去重和快速查找唯一元素;multiset
則更適合用于統計元素的出現次數等場景。 - pair 對組創建
語法:可以使用make_pair
函數或直接初始化的方式創建pair
對組。例如,pair<int, string> p = make_pair(1, "one");
或pair<int, string> p1(2, "two");
。pair
對組常用于在set
和multimap
等關聯容器中存儲鍵值對。 - set 容器排序
set
容器默認按照元素的升序進行排序,這是由其底層的紅黑樹結構和比較函數決定的。如果需要改變排序規則,可以自定義比較函數。例如:
struct Compare {bool operator()(int a, int b) {return a > b; // 降序排序}
};
set<int, Compare> s2;
????????通過這種方式,創建的set
容器s2
將按照降序對元素進行排序。
(九)map/multimap 容器
- 基本概念
map
是一個以鍵值對(key - value)形式存儲數據的關聯容器,其中鍵是唯一的,并且會自動按照鍵的順序進行排序,底層通常使用紅黑樹實現。這種結構使得通過鍵查找值的效率很高,適用于需要快速查找和存儲鍵值對數據的場景,比如學生成績管理系統中,以學生學號為鍵,成績為值進行存儲和查詢。
multimap
與map
類似,也是存儲鍵值對,但它允許鍵重復,適用于一個鍵可以對應多個值的場景,例如一個單詞在文章中可能出現多次,需要記錄每次出現的位置,就可以使用multimap
以單詞為鍵,位置為值進行存儲。 - 構造和賦值
默認構造:map<int, string> m;
和multimap<int, string> mm;
分別創建一個空的map
和multimap
容器。
拷貝構造:map<int, string> m1(m);
和multimap<int, string> mm1(mm);
分別將已有的map
和multimap
容器進行復制。
賦值運算符重載:m = m1;
和mm = mm1;
分別實現map
和multimap
容器之間的賦值操作。 - 大小和交換
size 函數:用于獲取map
或multimap
中鍵值對的個數,int mapSize = m.size();
和int multimapSize = mm.size();
。
empty 函數:用于判斷map
或multimap
是否為空,bool isMapEmpty = m.empty();
和bool isMultimapEmpty = mm.empty();
。
swap 函數:swap(m, m1);
和swap(mm, mm1);
分別用于交換兩個map
或兩個multimap
容器的內容。
- 插入和刪除
insert 函數:有多種插入方式。例如,使用m.insert(make_pair(1, "one"));
或m.insert(std::pair<int, std::string>(1, "one"));
?向map
容器m
中插入鍵值對。在multimap
中插入方式類似,如mm.insert(make_pair(1, "value1"));
,由于multimap
允許鍵重復,相同鍵的鍵值對會依次插入。
erase 函數:在map
中,m.erase(1);
刪除鍵為 1 的鍵值對;m.erase(m.find(1));
通過找到鍵 1 對應的迭代器來刪除鍵值對。在multimap
中,mm.erase(1);
會刪除所有鍵為 1 的鍵值對,mm.erase(mm.find(1));
則只刪除找到的第一個鍵為 1 的鍵值對。 - 查找和統計
find 函數:map<int, string>::iterator it = m.find(1);
在map
?m
中查找鍵為 1 的鍵值對,如果找到,it
指向該鍵值對;否則it
等于m.end()
。在multimap
中類似,multimap<int, string>::iterator mit = mm.find(1);
用于查找鍵為 1 的鍵值對。
count 函數:在map
中,m.count(1)
返回值只能是 0 或 1,因為鍵唯一。在multimap
中,mm.count(1)
用于統計鍵為 1 的鍵值對出現的次數,若存在多個鍵為 1 的鍵值對,會返回相應的數量。 - map 容器排序
map
默認根據鍵的升序進行排序,這基于其底層紅黑樹結構和默認比較函數。若要改變排序規則,可自定義比較函數。如:
struct KeyCompare {bool operator()(const int& a, const int& b) {return a > b; // 實現鍵的降序排序}
};
map<int, string, KeyCompare> m2;
????????如此創建的map
?m2
會按照鍵的降序對鍵值對進行排序。
7.?案例 - 員工分組
案例描述:假設有一個公司,需要將員工按照部門進行分組管理。每個員工有唯一的工號和所屬部門信息,要求能夠高效地存儲員工信息,并能根據部門快速查詢該部門的所有員工。此案例體現了map
或multimap
在實際場景中的應用。
實現步驟:
定義員工數據結構:
struct Employee {int id;std::string name;std::string department;Employee(int i, const std::string& n, const std::string& d) : id(i), name(n), department(d) {}
};
數據錄入:使用multimap
來存儲員工信息,因為一個部門可能有多個員工。例如:
multimap<std::string, Employee> employeeMap;
Employee emp1(1, "Alice", "HR");
Employee emp2(2, "Bob", "Engineering");
employeeMap.insert(make_pair("HR", emp1));
employeeMap.insert(make_pair("Engineering", emp2));
分組操作:根據部門對員工進行分組。例如,要獲取 “HR” 部門的所有員工,可以通過查找鍵 “HR” 來遍歷該部門的所有員工:
auto range = employeeMap.equal_range("HR");
for (auto it = range.first; it != range.second; ++it) {std::cout << "Employee ID: " << it->second.id << ", Name: " << it->second.name << std::endl;
}
結果輸出:將分組后的員工信息按照一定格式輸出,如上述代碼中,將 “HR” 部門的員工工號和姓名輸出顯示,方便查看和管理。
五、STL - 函數對象
(一)函數對象
- 概念
函數對象是一種可調用對象,通過在類中重載()
運算符來實現。它與普通函數的區別在于,函數對象可以攜帶狀態,即它可以包含成員變量來存儲額外的信息,而普通函數通常不具備這種能力。函數對象可以像普通函數一樣被調用,同時又具有類的特性,能夠進行數據封裝和繼承等操作。 - 使用
在 STL 算法中,函數對象常作為參數傳遞,用于定制算法的操作邏輯。例如,在sort
算法中,可以使用自定義的函數對象作為比較函數,實現特定的排序規則。假設有一個自定義的類MyComparator
:
class MyComparator {
public:bool operator()(const int& a, const int& b) {return a > b; // 實現降序比較}
};
在使用sort
函數時,可以這樣調用:
vector<int> numbers = {1, 3, 2, 4, 5};
sort(numbers.begin(), numbers.end(), MyComparator());
這里MyComparator()
作為比較函數傳遞給sort
算法,使得numbers
向量按照降序排列。
(二)謂詞
- 概念
謂詞是一種特殊的函數對象,其返回值為bool
類型,主要用于條件判斷等場景。在 STL 算法中,謂詞常用于篩選符合特定條件的元素或進行元素之間的比較。 - 一元謂詞
一元謂詞是接受一個參數并返回bool
值的函數對象。例如,定義一個一元謂詞IsEven
來判斷一個數是否為偶數:
class IsEven {
public:bool operator()(const int& num) {return num % 2 == 0;}
};
在使用find_if
算法查找向量中第一個偶數時,可以這樣應用:
vector<int> nums = {1, 3, 4, 5, 6};
auto it = find_if(nums.begin(), nums.end(), IsEven());
if (it != nums.end()) {std::cout << "First even number: " << *it << std::endl;
}
- 二元謂詞
二元謂詞是接受兩個參數并返回bool
值的函數對象。在排序算法中,常用二元謂詞來定義元素之間的比較規則。如前面提到的MyComparator
類就是一個二元謂詞,它接受兩個int
類型參數并返回比較結果,用于定義排序順序。
(三)內建函數對象
- 意義
內建函數對象是 STL 提供的一些預定義的函數對象,它們涵蓋了常見的算術、關系和邏輯操作。這些內建函數對象的存在提高了代碼編寫的效率,開發者無需手動編寫這些基本操作的函數對象,直接使用即可。 - 算術仿函數
加法仿函數plus
:plus<int>()(3, 5)
返回3 + 5
的結果,即 8。在accumulate
算法中結合plus
仿函數可用于對容器元素求和,例如:
vector<int> values = {1, 2, 3, 4, 5};
int sum = accumulate(values.begin(), values.end(), 0, plus<int>());
????????減法仿函數minus
:minus<int>()(5, 3)
返回5 - 3
的結果,即 2。可用于實現兩個數的減法操作,在一些自定義算法中可能會用到。
乘法仿函數multiplies
:multiplies<int>()(3, 4)
返回3 * 4
的結果,即 12。在數值計算相關的算法中可能會使用到。
????????除法仿函數divides
:divides<int>()(6, 3)
返回6 / 3
的結果,即 2。用于實現除法運算。
????????取模仿函數modulus
:modulus<int>()(7, 3)
返回7 % 3
的結果,即 1。在需要進行取模運算的場景中使用。
3.?關系仿函數
????????大于仿函數greater
:greater<int>()(5, 3)
返回true
,因為5 > 3
。在sort
算法中,如果要對向量進行降序排序,可以使用sort(numbers.begin(), numbers.end(), greater<int>());
小于仿函數less
:less<int>()(3, 5)
返回true
,因為3 < 5
。這是sort
算法默認的比較方式,用于升序排序。
????????大于等于仿函數greater_equal
:greater_equal<int>()(5, 5)
返回true
,因為5 >= 5
。在一些需要判斷大于等于關系的算法中使用。
小于等于仿函數less_equal
:less_equal<int>()(3, 3)
返回true
,因為3 <= 3
。常用于條件判斷場景。
????????等于仿函數equal_to
:equal_to<int>()(3, 3)
返回true
,用于判斷兩個數是否相等。在查找算法中可能會用到,如find_if
結合equal_to
來查找特定值。
不等于仿函數not_equal_to
:not_equal_to<int>()(3, 5)
返回true
,因為3 != 5
。在需要判斷不相等關系的場景中使用。
4.?邏輯仿函數
????????邏輯與仿函數logical_and
:logical_and<bool>()(true, false)
返回false
,因為true && false
為false
。在一些需要進行邏輯與運算的算法中使用,如在對容器元素進行條件篩選時。
邏輯或仿函數logical_or
:logical_or<bool>()(true, false)
返回true
,因為true || false
為true
。用于邏輯或運算場景,例如多個條件判斷中只要有一個條件滿足的情況。
邏輯非仿函數logical_not
:logical_not<bool>()(true)
返回false
,因為!true
為false
。用于對邏輯值取反的操作。
六、STL - 常用算法
(一)常用遍歷算法
- for_each
語法:for_each(iterator start, iterator end, function object)
。它接受一個迭代器范圍(起始迭代器start
和結束迭代器end
)以及一個函數對象。
功能:對容器中指定范圍內的每個元素逐一應用傳入的函數對象。例如,假設有一個打印整數的函數對象PrintNumber
:
class PrintNumber {
public:void operator()(const int& num) {std::cout << num << " ";}
};
vector<int> nums = {1, 2, 3, 4, 5};
for_each(nums.begin(), nums.end(), PrintNumber());
上述代碼會將nums
向量中的每個元素依次打印出來。
應用場景:常用于對容器元素進行簡單的遍歷操作,如打印元素、對每個元素進行簡單的計算等。
2.?transform
語法:transform(iterator start, iterator end, output_iterator, function object)
或transform(iterator1 start, iterator1 end, iterator2 start, output_iterator, function object)
。第一種形式將輸入范圍[start, end)
內的元素通過函數對象進行轉換,并將結果存儲到以output_iterator
開始的位置;第二種形式從兩個輸入范圍(分別以iterator1 start
和iterator2 start
開始)中取出元素,通過函數對象進行轉換,并將結果存儲到output_iterator
開始的位置。
功能:對容器元素進行轉換并存儲到新容器或原容器。例如,將一個向量中的每個元素乘以 2 并存儲到另一個向量中:
class MultiplyByTwo {
public:int operator()(const int& num) {return num * 2;}
};
vector<int> source = {1, 2, 3, 4, 5};
vector<int> result(source.size());
transform(source.begin(), source.end(), result.begin(), MultiplyByTwo());
這里MultiplyByTwo
函數對象將source
向量中的每個元素乘以 2,并將結果存儲到result
向量中。
應用場景:在數據轉換、類型轉換等場景中廣泛應用,如將字符串向量中的所有字符串轉換為大寫形式,或者將一個數值向量中的每個元素進行特定的數學運算后存儲到新向量。
(二)常用查找算法
- find
語法:find(iterator start, iterator end, value)
。它在指定的迭代器范圍[start, end)
內查找值為value
的元素。
功能:在容器中查找指定值。例如,在一個向量中查找值為 3 的元素:
vector<int> numbers = {1, 2, 3, 4, 5};
auto it = find(numbers.begin(), numbers.end(), 3);
if (it != numbers.end()) {std::cout << "Element 3 found at position: " << std::distance(numbers.begin(), it) << std::endl;
}
如果找到元素,find
函數返回指向該元素的迭代器;否則返回end
迭代器。
應用場景:常用于判斷容器中是否存在特定元素,或者獲取特定元素在容器中的位置。
2.?find_if
語法:find_if(iterator start, iterator end, predicate)
。它在指定的迭代器范圍[start, end)
內查找滿足謂詞predicate
的第一個元素。
功能:在容器中查找滿足條件的元素。例如,查找向量中第一個大于 3 的元素:
class GreaterThanThree {
public:bool operator()(const int& num) {return num > 3;}
};
vector<int> nums = {1, 2, 4, 5, 3};
auto it1 = find_if(nums.begin(), nums.end(), GreaterThanThree());
if (it1 != nums.end()) {std::cout << "First element greater than 3: " << *it1 << std::endl;
}
應用場景:在需要按特定條件篩選元素時使用,如在一個學生成績向量中查找第一個不及格的成績。
3.?adjacent_find
語法:adjacent_find(iterator start, iterator end)
或adjacent_find(iterator start, iterator end, binary_predicate)
。第一種形式查找相鄰且相等的元素;第二種形式使用自定義的二元謂詞查找滿足特定條件的相鄰元素。
功能:查找相鄰且相等的元素。例如,在一個向量中查找相鄰重復的元素:
vector<int> values = {1, 2, 2, 3, 4};
auto it2 = adjacent_find(values.begin(), values.end());
if (it2 != values.end()) {std::cout << "Adjacent duplicate element found: " << *it2 << std::endl;
}
如果找到相鄰重復元素,返回指向第一個重復元素的迭代器;否則返回end
迭代器。
應用場景:用于檢測序列中是否存在相鄰重復的元素,在數據去重、數據校驗等場景中可能會用到。
4.?binary_search
語法:binary_search(iterator start, iterator end, value)
或binary_search(iterator start, iterator end, value, compare)
。第一種形式在有序的迭代器范圍[start, end)
內使用默認比較函數二分查找值為value
的元素;第二種形式使用自定義的比較函數compare
進行二分查找。
功能:在有序容器中二分查找指定值。例如,在一個有序向量中查找值為 4 的元素:
vector<int> sortedNums = {1, 2, 3, 4, 5};
bool found = binary_search(sortedNums.begin(), sortedNums.end(), 4);
if (found) {std::cout << "Element 4 found in the sorted vector." << std::endl;
}
如果找到元素,返回true
;否則返回false
。
應用場景:在需要高效查找有序序列中的元素時使用,相比順序查找,二分查找的時間復雜度更低,適用于大規模數據的查找。
5.?count
語法:count(iterator start, iterator end, value)
。它在指定的迭代器范圍[start, end)
內統計值為value
的元素個數。
功能:統計容器中指定值出現的次數。例如,在一個向量中統計值為 2 的元素個數:
vector<int> numbers = {1, 2, 2, 3, 2, 4};
int countVal = count(numbers.begin(), numbers.end(), 2);
std::cout << "The number 2 appears " << countVal << " times." << std::endl;
應用場景:常用于統計元素頻率,如統計一篇文章中某個單詞出現的次數。
6.?count_if
語法:count_if(iterator start, iterator end, predicate)
。它在指定的迭代器范圍[start, end)
內統計滿足謂詞predicate
的元素個數。
功能:統計容器中滿足條件的元素個數。例如,統計向量中大于 3 的元素個數:
class GreaterThanThree {
public:bool operator()(const int& num) {return num > 3;}
};
vector<int> nums = {1, 2, 4, 5, 3, 6};
int countGreater = count_if(nums.begin(), nums.end(), GreaterThanThree());
std::cout << "The number of elements greater than 3 is: " << countGreater << std::endl;
應用場景:在需要按條件統計元素數量時使用,如統計一個
班級成績中及格人數、統計一組數據中奇數的數量等場景。
(三)常用排序算法
- sort
語法:sort(iterator start, iterator end)
?或?sort(iterator start, iterator end, comparator)
。第一種形式使用默認的小于比較函數(less
)對迭代器范圍?[start, end)
?內的元素進行升序排序;第二種形式則使用自定義的比較函數?comparator
?來決定排序規則。
功能:對容器元素進行排序。例如,對一個存儲整數的向量進行升序排序:
std::vector<int> numbers = {5, 3, 7, 1, 9};
std::sort(numbers.begin(), numbers.end());
若要進行降序排序,可自定義比較函數:
struct Greater {bool operator()(const int& a, const int& b) {return a > b;}
};
std::vector<int> numbers = {5, 3, 7, 1, 9};
std::sort(numbers.begin(), numbers.end(), Greater());
應用場景:廣泛應用于各種需要對數據進行排序的場景,如對學生成績進行排名、對商品價格進行排序展示等。
2.?random_shuffle
語法:random_shuffle(iterator start, iterator end)
。它對指定迭代器范圍?[start, end)
?內的元素進行隨機重排。
功能:隨機打亂容器元素順序。例如,將一副撲克牌(用向量表示)的順序打亂:
std::vector<int> deck(52);
for (int i = 0; i < 52; ++i) {deck[i] = i;
}
std::random_shuffle(deck.begin(), deck.end());
應用場景:在需要隨機化數據順序的場景中使用,如游戲中的隨機發牌、隨機分組等。
3.?merge
語法:merge(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator)
?或?merge(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator, comparator)
。第一種形式將兩個有序的輸入范圍(分別由?iterator1 start
?到?iterator1 end
?和?iterator2 start
?到?iterator2 end
?定義)合并到以?output_iterator
?開始的輸出范圍,使用默認的小于比較函數;第二種形式則使用自定義的比較函數?comparator
?進行合并。
功能:合并兩個有序容器。例如,將兩個有序的整數向量合并成一個新的有序向量:
std::vector<int> v1 = {1, 3, 5};
std::vector<int> v2 = {2, 4, 6};
std::vector<int> result(v1.size() + v2.size());
std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), result.begin());
應用場景:在數據處理中,當需要將多個有序數據集合并成一個更大的有序數據集時使用,如合并多個有序的日志文件。
4.?reverse
語法:reverse(iterator start, iterator end)
。它將指定迭代器范圍?[start, end)
?內的元素順序反轉。
功能:反轉容器元素順序。例如,將一個字符串(用?string
?容器表示)反轉:
std::string str = "hello";
std::reverse(str.begin(), str.end());
應用場景:在需要改變序列順序的場景中使用,如將鏈表反轉、將數字的各位順序反轉等。
(四)常用拷貝和替換算法
- copy
語法:copy(iterator start, iterator end, output_iterator)
。它將迭代器范圍?[start, end)
?內的元素拷貝到以?output_iterator
?開始的位置。
功能:將容器元素拷貝到另一個位置。例如,將一個向量的元素拷貝到另一個向量:
std::vector<int> source = {1, 2, 3, 4, 5};
std::vector<int> destination(source.size());
std::copy(source.begin(), source.end(), destination.begin());
應用場景:常用于數據備份、容器間元素復制等場景,如將一個臨時計算結果向量復制到最終存儲向量。
2.?replace
語法:replace(iterator start, iterator end, old_value, new_value)
。它將迭代器范圍?[start, end)
?內所有值為?old_value
?的元素替換為?new_value
。
功能:將容器中指定值替換為新值。例如,將一個向量中所有的 3 替換為 30:
std::vector<int> numbers = {1, 3, 2, 3, 4};
std::replace(numbers.begin(), numbers.end(), 3, 30);
應用場景:在數據修正場景中使用,如將文本中錯誤的單詞統一替換為正確的單詞。
3.?replace_if
語法:replace_if(iterator start, iterator end, predicate, new_value)
。它將迭代器范圍?[start, end)
?內所有滿足謂詞?predicate
?的元素替換為?new_value
。
功能:將容器中滿足條件的值替換為新值。例如,將一個向量中所有大于 5 的元素替換為 0:
class GreaterThanFive {
public:bool operator()(const int& num) {return num > 5;}
};
std::vector<int> numbers = {1, 3, 7, 4, 8};
std::replace_if(numbers.begin(), numbers.end(), GreaterThanFive(), 0);
應用場景:在按條件修改數據時使用,如將一組成績中不及格的分數替換為補考標記。
4.?swap
語法:swap(value1, value2)
。它交換兩個值?value1
?和?value2
?的內容。
功能:交換兩個值。例如,交換兩個整數變量的值:
int a = 5;
int b = 3;
std::swap(a, b);
在容器場景中,也可用于交換兩個容器的內容,如?std::swap(v1, v2);
?交換兩個向量?v1
?和?v2
?的內容。
應用場景:元素交換、容器內容交換等場景,如在排序算法中用于交換元素位置,在需要快速切換兩個數據集時交換容器內容。
(五)常用算術生成算法
- accumulate
語法:accumulate(iterator start, iterator end, initial_value)
?或?accumulate(iterator start, iterator end, initial_value, binary_function)
。第一種形式將迭代器范圍?[start, end)
?內的元素累加,并加上初始值?initial_value
,使用默認的加法操作;第二種形式則使用自定義的二元函數?binary_function
?進行累加操作。
功能:對容器元素進行累加求和。例如,計算一個向量中所有元素的和:
std::vector<int> numbers = {1, 2, 3, 4, 5};
int sum = std::accumulate(numbers.begin(), numbers.end(), 0);
若要進行乘法累加(即計算所有元素的乘積),可自定義二元函數:
struct Multiply {int operator()(const int& a, const int& b) {return a * b;}
};
std::vector<int> numbers = {1, 2, 3, 4, 5};
int product = std::accumulate(numbers.begin(), numbers.end(), 1, Multiply());
應用場景:數值計算場景,如計算一組數據的總和、平均值等,在金融計算、統計分析等領域有廣泛應用。
2.?fill
語法:fill(iterator start, iterator end, value)
。它將迭代器范圍?[start, end)
?內的所有元素填充為指定值?value
。
功能:將容器指定范圍填充為指定值。例如,將一個向量的所有元素初始化為 0:
std::vector<int> numbers(5);
std::fill(numbers.begin(), numbers.end(), 0);
應用場景:在初始化容器元素、重置容器內容等場景中使用,如在進行多次數據處理前,將結果容器清空并填充為初始值。
(六)常用集合算法
- set_intersection
語法:set_intersection(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator)
?或?set_intersection(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator, comparator)
。第一種形式求兩個有序范圍(分別由?iterator1 start
?到?iterator1 end
?和?iterator2 start
?到?iterator2 end
?定義)的交集,并將結果存儲到以?output_iterator
?開始的位置,使用默認的小于比較函數;第二種形式使用自定義的比較函數?comparator
。
功能:求兩個有序集合的交集。例如,求兩個有序向量的交集:
std::vector<int> v1 = {1, 3, 5, 7};
std::vector<int> v2 = {3, 5, 7, 9};
std::vector<int> intersection;
std::set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(intersection));
應用場景:在需要找出兩個數據集的共同部分時使用,如找出兩個學生名單中的共同學生、兩個商品清單中的重復商品等。
2.?set_union
語法:set_union(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator)
?或?set_union(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator, comparator)
。第一種形式求兩個有序范圍的并集,并將結果存儲到以?output_iterator
?開始的位置,使用默認的小于比較函數;第二種形式使用自定義的比較函數?comparator
。
功能:求兩個有序集合的并集。例如,求兩個有序向量的并集:
std::vector<int> v1 = {1, 3, 5};
std::vector<int> v2 = {3, 5, 7};
std::vector<int> unionSet;
std::set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(unionSet));
應用場景:在合并兩個數據集且去除重復元素時使用,如合并兩個課程列表并去除重復課程。
3.?set_difference
語法:set_difference(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator)
?或?set_difference(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator, comparator)
。第一種形式求第一個有序范圍相對于第二個有序范圍的差集(即存在于第一個范圍但不存在于第二個范圍的元素),并將結果存儲到以?output_iterator
?開始的位置,使用默認的小于比較函數;第二種形式使用自定義的比較函數?comparator
。
功能:求兩個有序集合的差集。例如,求向量?v1
?相對于向量?v2
?的差集:
std::vector<int> v1 = {1, 3, 5, 7};
std::vector<int> v2 = {3, 5};
std::vector<int> difference;
std::set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(difference));
應用場景:在需要找出兩個數據集的差異部分時使用,如找出兩個版本軟件功能列表的新增功能(相對于舊版本)。
七、總結
??C++ 提高編程中的模板編程和 STL 為開發者提供了強大的工具集。模板編程通過類型參數化實現了代碼的高度復用,無論是函數模板還是類模板,都極大地減少了重復代碼的編寫,提高了代碼的通用性和可維護性。STL 作為一套成熟的模板庫,其六大組件相互協作,容器提供了多樣化的數據存儲結構,算法實現了豐富的數據處理操作,迭代器則作為橋梁連接了容器和算法,使得代碼能夠高效、靈活地處理各種數據場景。通過對常用容器、函數對象和常用算法的深入理解和應用,開發者能夠顯著提升 C++ 編程的效率和質量,編寫出更健壯、更高效的代碼。在實際項目開發中,合理運用這些技術能夠降低開發成本,提高軟件的性能和可擴展性,滿足日益復雜的軟件需求。因此,深入學習和掌握模板編程與 STL 是 C++ 開發者提升自身能力的關鍵路徑。
- copy
語法:copy(iterator start, iterator end, output_iterator)
。它將迭代器范圍?[start, end)
?內的元素拷貝到以?output_iterator
?開始的位置。
功能:將容器元素拷貝到另一個位置。例如,將一個向量的元素拷貝到另一個向量: - accumulate
語法:accumulate(iterator start, iterator end, initial_value)
?或?accumulate(iterator start, iterator end, initial_value, binary_function)
。第一種形式將迭代器范圍?[start, end)
?內的元素累加,并加上初始值?initial_value
,使用默認的加法操作;第二種形式則使用自定義的二元函數?binary_function
?進行累加操作。
功能:對容器元素進行累加求和。例如,計算一個向量中所有元素的和: - set_intersection
語法:set_intersection(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator)
?或?set_intersection(iterator1 start, iterator1 end, iterator2 start, iterator2 end, output_iterator, comparator)
。第一種形式求兩個有序范圍(分別由?iterator1 start
?到?iterator1 end
?和?iterator2 start
?到?iterator2 end
?定義)的交集,并將結果存儲到以?output_iterator
?開始的位置,使用默認的小于比較函數;第二種形式使用自定義的比較函數?comparator
。
功能:求兩個有序集合的交集。例如,求兩個有序向量的交集: