文章目錄
- 一、模板
- 1.1 定義
- 1.2 作用
- 1.3 函數模版
- 1.3.1 格式
- 1.4 類模版
- 1.4.1 格式
- 1.4.2 代碼示例
- 1.4.3 特性
- 二、容器
- 2.1 概念
- 2.2 容器特性
- 2.3 分類
- 2.4 向量vector
- 2.4.1 特性
- 2.4.2 初始化與操作
- 2.4.3 插入刪除
- 2.5 迭代器
- 2.6 列表(list)
- 2.6.1 遍歷方式
- 2.6.2 插入與刪除操作
- 2.7隊列(Deque)
- 2.7.1定義與基本特性
- 2.7.2 內部實現機制
- 2.8. map 容器
- 2.8.1 特性
- 2.8.2 定義格式
- 2.8.3 遍歷方式
- 2.8.4 插入刪除查找操作
- 三、C++異常處理詳解
- 3.1 異常基本概念
- 3.2 異常處理機制
- 3.3 簡易異常實現
- 3.4 標準異常
- 3.5 自定義異常
- 四、文件流
- 總結
一、模板
1.1 定義
C++模板是支持參數化多態的工具,允許類或函數聲明為通用類型,使得其數據成員、參數或返回值在實際使用時可以是任意類型。模板通過編譯器生成具體代碼,實現類型無關的邏輯復用。
1.2 作用
實現泛型編程,提高代碼的通用性和復用性。
避免為不同數據類型重復編寫相同邏輯的代碼。
1.3 函數模版
1.3.1 格式
template <typename T1, typename T2, ...>
返回類型 函數名(參數列表) {// 函數體
}
- 關鍵字 typename 或 class 均可用于聲明類型參數,typename 更推薦(尤其在處理依賴類型時)。
代碼示例
#include<iostream>
using namespace std;// 通用加法函數模板
// T: 第一個參數和返回值的類型
// C: 第二個參數的類型
// 當T和C類型不同時,返回值類型與第一個參數類型保持一致
template <class T, class C>
T add(T a, C b) {return a + b;
}int main() {// 測試整型相加(int + int)cout << add(1, 2) << endl;// 測試浮點型相加(double + double)cout << add(1.1, 2.2) << endl;// 測試字符類型相加(char + char),ASCII碼值運算cout << add('A',' ') << endl;// 測試字符串拼接(string + string)string s1 = "Hello";string s2 = "World";cout << add(s1, s2) << endl;// 測試混合類型相加(int + char),返回int類型cout << add(32, 'A') << endl;// 測試混合類型相加(double + int),返回double類型cout << add(32.1, 2) << endl;return 0;
1.4 類模版
1.4.1 格式
template <typename T1, typename T2, ...>
class 類名 {// 類成員
};
1.4.2 代碼示例
#include<iostream>
using namespace std;// 定義一個模板類Test,T為泛型類型參數
template <class T>
class Test{
public:// 構造函數,使用類型T的參數初始化成員變量numTest(T n):num(n){}// 聲明公有成員函數getVal,返回類型為T,const表示不修改對象狀態T getVal()const;private:T num; // 私有成員變量,類型為T
};// 類外定義模板成員函數getVal()
// 注意模板類成員函數在類外定義時需要再次聲明模板參數
template <class T>
T Test<T>::getVal()const{return num; // 返回成員變量num的值
}int main()
{// 實例化Test類,指定類型參數為int,并初始化值為100Test<int> tt(100);// 調用getVal()獲取存儲的值并輸出cout << tt.getVal() << endl; // 輸出: 100// 實例化Test類,指定類型參數為float,并初始化值為3.14Test<float> t1(3.14);cout << t1.getVal() <<endl; // 輸出: 3.14return 0;
}
1.4.3 特性
- 作用域限制:模板只能在全局、命名空間或類作用域中聲明,不能在局部作用域(如函數內)定義。
- 成員函數定義:類外定義時需重復模板聲明,并使用 類名:: 限定作用域。
- 顯式實例化:使用類模板時必須指定具體類型參數。
二、容器
2.1 概念
C++標準模板庫(STL)提供基于模板的通用組件,包含容器、迭代器、算法等核心部件,用于高效處理數據存儲和操作任務。
2.2 容器特性
類型一致性: 容器內元素類型必須相同(與Python不同)
內存管理:
默認使用棧內存,無需手動new/delete
動態容器(如vector)自動管理堆內存
操作效率: 不同容器對插入/刪除/隨機訪問的性能差異顯著
2.3 分類
類型 | 容器 | 特性 |
---|---|---|
順序容器 | vector | 動態數組,連續內存,隨機訪問高效 |
list | 雙向鏈表,非連續內存,插入刪除高效 | |
deque | 雙端隊列,分段連續內存,首尾操作高效 | |
關聯容器 | map | 紅黑樹實現,鍵值對有序存儲,鍵唯一 |
set | 有序唯一鍵集合 | |
unordered_map | 哈希表實現,無序存儲,平均O(1)訪問 |
2.4 向量vector
2.4.1 特性
底層結構: 動態數組
時間復雜度:
隨機訪問:O(1)
尾部操作:O(1)
中間插入/刪除: O(n)
頭文件: #include < vector >
2.4.2 初始化與操作
#include <vector>
using namespace std;
int main()
{// 定義一個空的vector對象v1,使用默認構造函數初始化vector <int>v1;// 定義一個有5個元素的向量v2,元素進行值初始化(int類型默認初始化為0)vector <int>v2(5);// 定義一個有5個元素的向量v3,每個元素的初始值均為4vector <int>v3(5, 4);// 使用C++11的列表初始化定義向量v4,直接包含元素{5,6,4,3,2}// 此語法需要編譯器支持C++11及以上標準vector <int>v4{5,6,4,3,2};
}
在QT編譯中支持C++11版本的修改方式:
在.pro中增加下面內容:
CONFIG += c++11
#include<iostream>
#include <vector>
using namespace std;int main()
{// 使用C++11的列表初始化語法創建并初始化vectorvector <int> v4{5, 6, 4, 3, 2};// 使用傳統的for循環遍歷vectorfor(int i = 0; i < v4.size(); i++){// 使用at()成員函數訪問元素,at()會進行邊界檢查,更安全cout << v4.at(i) << endl;}// 使用下標操作符修改vector中的元素// 注意:下標操作符不進行邊界檢查,訪問越界會導致未定義行為v4[3] = 100;// 使用C++11的范圍for循環遍歷vector// 這種語法更簡潔,i依次獲取v4中的每個元素的副本for(int i : v4){cout << i << endl;}return 0; // 表示程序正常結束
}
2.4.3 插入刪除
int main()
{// 創建并初始化一個整型vector容器vector<int> v4{5, 6, 4, 3, 2};// 清除容器(當前被注釋)// v4.clear();// 判斷容器是否為空(1表示空,0表示非空)(當前被注釋)// cout << v4.empty() << endl;// 在容器尾部追加元素(注意類型轉換)v4.push_back(10); // 直接追加整型v4.push_back(20.5); // double類型會被截斷為整型20v4.push_back('a'); // char類型會轉換為ASCII碼97// 迭代器操作(當前被注釋)// cout << *(v4.begin()+2) << endl; // 訪問第3個元素(下標2)// cout << *(v4.end()-1) << endl; // 訪問最后一個元素// 在容器頭部插入元素222v4.insert(v4.begin(), 222);// 在倒數第三個位置插入元素333v4.insert(v4.end() - 2, 333);// 移除容器最后一個元素(當前會移除ASCII碼97對應的元素)v4.pop_back();// 移除容器第一個元素(當前會移除頭部元素222)v4.erase(v4.begin());// 遍歷輸出容器當前所有元素// 最終元素順序:5,6,4,3,2,10,20,333,97 -> 經過刪除操作后變為:// 6,4,3,2,10,333,20(注意實際執行后建議調試驗證)for(int i : v4) {cout << i << endl;}return 0;
}
2.5 迭代器
類型:
iterator:可讀寫
const_iterator:只讀
reverse_iterator:反向遍歷
失效問題: 容器結構變更(如vector擴容)可能導致迭代器失效
容器類型 <數據類型>::iterator 迭代器變量名;
容器類型 <數據類型>::const_iterator 迭代器變量名;
#include<iostream> // 引入輸入輸出流庫
#include <vector> // 引入向量容器庫
using namespace std; // 使用標準命名空間,避免std::前綴int main()
{// 創建并初始化整型向量v4,包含元素5,6,4,3,2vector <int>v4{5,6,4,3,2};// 定義可讀寫的迭代器,指向向量v4的起始位置vector <int>::iterator it = v4.begin();// 若使用const_iterator則無法通過迭代器修改元素(注釋掉的備用方案)// vector <int>::const_iterator it = v4.begin();// 通過迭代器修改向量第一個元素的值*it = 100;// 使用迭代器遍歷向量:從當前位置(it)開始,直到超過末尾(end)for(; it < v4.end();it++)cout << *it << endl; // 解引用迭代器,輸出當前元素值return 0; // 程序正常退出
}
2.6 列表(list)
列表是C++標準模板庫(STL)中的雙向鏈表實現。其特點如下:
內部結構: 由雙向鏈表實現,元素內存地址不連續,通過指針鏈接。
訪問特性:
不支持隨機訪問(不能使用at()或[]操作符)。
需通過迭代器順序訪問,迭代器類型為雙向迭代器(僅支持++、–操作)。
性能特點:
擅長任意位置的插入和刪除操作(時間復雜度為 O(1),但需先遍歷到操作位置)。
與vector功能互補: vector長于隨機訪問和尾部操作,list長于頻繁插入刪除。
2.6.1 遍歷方式
#include<iostream> // 輸入輸出流頭文件
#include<list> // 列表容器頭文件
using namespace std; // 標準命名空間int main()
{list <int>ls{4, 3, 2, 5, 1, 6}; // 初始化雙向鏈表容器// 通過雙向迭代器進行遍歷(不支持隨機訪問)list <int>::iterator it = ls.begin(); // 獲取起始迭代器// list迭代器只能使用前/后遞增運算符移動// 注意:迭代器自增后指向第二個元素(3)cout << *(++it) << endl; // 輸出:3// 從第二個元素開始遍歷(當前迭代器已自增)for(; it != ls.end(); it++){ // 遍歷直到鏈表末尾cout << *it << endl; // 輸出:3 → 2 → 5 → 1 → 6}return 0;
}
2.6.2 插入與刪除操作
基礎操作(與vector類似):
list<int> ls{4, 3, 2, 5, 1, 6};
// 頭部操作
ls.push_front(100); // 頭部插入
ls.pop_front(); // 頭部刪除
// 尾部操作
ls.push_back(99); // 尾部插入
ls.pop_back(); // 尾部刪除
// 訪問首尾元素
cout << "Head: " << ls.front() << endl; // 輸出頭部元素
cout << "Tail: " << ls.back() << endl; // 輸出尾部元素
列表特有操作:
排序:
ls.sort(); // 默認升序排序(成員函數,時間復雜度O(n log n))
注意:需使用成員函數sort(),而非標準庫的std::sort()(因后者需隨機訪問迭代器)。
去重:
ls.unique(); // 移除相鄰重復元素(通常先排序后使用)
合并鏈表:
list<int> ls2{7, 8, 9};
ls.merge(ls2); // 合并后ls2為空,ls包含合并后的有序元素(需雙方已排序)
刪除特定值:
ls.remove(3); // 刪除所有值為3的元素
反轉鏈表:
ls.reverse(); // 反轉鏈表順序
2.7隊列(Deque)
2.7.1定義與基本特性
雙端隊列(Double-Ended Queue,Deque)是一種允許在頭部和尾部高效插入、刪除元素的線性數據結構。它結合了向量(動態數組)和鏈表的優點,支持兩端操作的時間復雜度為 ,同時提供接近向量的隨機訪問性能。
2.7.2 內部實現機制
分塊存儲結構
Deque 內部由多個固定大小的數組塊組成,每個塊獨立分配內存。
當頭部或尾部空間不足時,僅需分配新塊并鏈接,無需整體復制(如向量擴容)。
例如:一個典型的實現可能維護一個中央索引數組,記錄所有塊的地址,通過計算快速定位元素。
高效的兩端操作
頭部插入/刪除:若當前頭塊未滿,直接操作;否則分配新塊。
尾部插入/刪除:類似頭部邏輯,尾部塊不足時擴展新塊。
2.8. map 容器
2.8.1 特性
關聯容器: 鍵值對(Key-Value)存儲,類似 Python 的 dict。
底層實現: 基于紅黑樹(自平衡二叉搜索樹),保證元素按鍵有序(默認升序)。
時間復雜度: 插入、刪除、查找操作的平均和最壞時間復雜度均為 O(log n)。
鍵的特性:
唯一性: 鍵不可重復。
有序性: 鍵自動排序,需支持比較操作(默認 operator<)。
迭代器穩定性:
插入操作不會使迭代器失效(除非容器被銷毀)。
刪除操作僅影響被刪除元素的迭代器。
2.8.2 定義格式
#include <map>
using namespace std;// 默認定義(升序)
map<KeyType, ValueType> myMap;// 自定義排序規則
struct Compare {bool operator()(const KeyType& a, const KeyType& b) const {return a > b; // 降序排列示例}
};
map<KeyType, ValueType, Compare> customOrderMap;
2.8.3 遍歷方式
#include<iostream> // 輸入輸出流頭文件
#include<map> // 關聯容器頭文件(鍵值對存儲)
using namespace std; // 標準命名空間int main()
{// 初始化map容器(鍵:string類型,值:int類型)map <string, int> mp = {{"num",101},{"age", 20}};// 通過下標運算符修改值mp["num"] = 200; // 修改鍵"num"對應的值cout << mp["num"] << endl; // 輸出:200// 通過at方法安全訪問值(會檢查鍵是否存在)cout << mp.at("age") << endl; // 輸出:20// 通過雙向迭代器遍歷mapmap <string, int>::iterator it = mp.begin();for(; it != mp.end(); it++){// first訪問鍵,second訪問值cout << it->first << ":" << it->second << endl; // 輸出鍵值對}return 0;
}
2.8.4 插入刪除查找操作
#include<iostream> // 輸入輸出流頭文件
#include<map> // 關聯容器頭文件(鍵值對存儲)
using namespace std; // 標準命名空間int main()
{map <string, int> mp = {{"num",101},{"age", 20}};// 通過pair對象插入新鍵值對mp.insert(pair<string, int>("weight", 88)); // 顯式構造pair插入// 使用make_pair自動推導類型插入mp.insert(make_pair("name", 123)); // 更簡潔的pair創建方式// 通過鍵值刪除元素(刪除"age"對應的鍵值對)mp.erase("age"); // 返回刪除元素的數量// 查找鍵"num"并輸出對應值(需確保存在)cout << mp.find("num")->second << endl; // 輸出:101// 安全查找機制:驗證find結果是否為有效迭代器if(mp.find("num") != mp.end()){ // 判斷是否找到鍵mp["num"] = 18; // 安全修改值}else{cout << "沒有找到" << endl; // 未找到時的處理}return 0;
}
三、C++異常處理詳解
3.1 異常基本概念
定義:程序運行期間發生的非正常情況(如除零錯誤、越界訪問等)。
核心作用:通過throw拋出異常、try監控代碼塊、catch捕獲異常,實現錯誤控制權的轉移,避免程序崩潰。
處理必要性:未處理的異常會導致程序終止。
3.2 異常處理機制
關鍵字:throw、try、catch
語法結構:
try {// 可能拋出異常的代碼(保護代碼)
} catch (ExceptionType1& e1) {// 處理 ExceptionType1 類型異常
} catch (ExceptionType2& e2) {// 處理 ExceptionType2 類型異常
} catch (...) {// 捕獲所有其他類型異常
}
3.3 簡易異常實現
#include<iostream> // 輸入輸出流頭文件
using namespace std; // 標準命名空間// 帶異常檢查的除法函數
// 參數:a-被除數,b-除數
// 返回值:整型除法結果
int divs(int a, int b)
{if(b == 0){ // 檢查除數合法性string s("除數為0"); // 創建異常描述字符串throw s; // 拋出字符串類型異常}return a/b; // 執行安全除法運算
}int main()
{try{ // 異常監控代碼塊// 調用可能拋出異常的除法運算cout << divs(4, 0) << endl; // 觸發異常的調用點}catch(string &s){ // 捕獲字符串類型異常cout << s << endl; // 輸出異常描述信息}return 0; // 程序正常退出
}
3.4 標準異常
頭文件: < stdexcept >(提供常見異常類型如invalid_argument、out_of_range等)。
特點:
標準異常類繼承自std::exception,通過多態統一處理。
使用what()方法獲取錯誤信息。
示例:
#include<iostream> // 輸入輸出流頭文件
using namespace std; // 標準命名空間// 帶異常檢查的除法函數
// 參數:a-被除數,b-除數
// 返回值:整型除法結果
int divs(int a, int b)
{if(b == 0){ // 檢查除數合法性// 拋出標準庫異常對象(比原始字符串更規范)throw invalid_argument("除數為0"); // 構造帶錯誤信息的異常對象}return a/b; // 執行安全除法運算
}int main()
{try{ // 異常監控代碼塊// 調用可能拋出異常的除法運算cout << divs(4, 0) << endl; // 觸發異常的調用點}catch(exception &e){ // 通過基類引用捕獲所有派生異常// 調用虛函數what()獲取異常描述cout << e.what() << endl; // 輸出:除數為0}return 0; // 程序正常退出
}
3.5 自定義異常
實現步驟:
繼承自std::exception或其子類。
重寫what()方法,返回錯誤描述(需保證返回的字符串有效性)。
#include<iostream> // 輸入輸出流頭文件
using namespace std; // 標準命名空間// 自定義異常類(繼承標準異常體系)
class MyExcept:public exception{ // 繼承自exception基類
public:// 異常描述方法(符合C++標準異常規范)// 第一個const: 返回不可修改的字符串指針// 第二個const: 承諾不修改對象狀態// throw(): 空異常規格(C++11前表示不拋出任何異常)const char * what() const throw(){ // 重寫虛函數return "除數為0"; // 返回固定錯誤描述}
};// 帶異常檢查的除法運算
int divs(int a, int b)
{if(b == 0){ // 檢查除數合法性MyExcept me; // 創建異常對象實例throw me; // 拋出符合標準的異常對象}return a/b; // 執行安全除法運算
}int main()
{try{ // 異常監控代碼塊cout << divs(4, 0) << endl; // 觸發異常的調用點}catch(exception &e){ // 多態捕獲標準異常體系cout << e.what() << endl; // 通過虛函數獲取描述信息}return 0; // 程序正常退出
}
四、文件流
頭文件
#include <fstream> // 包含文件流操作所需的類
#include <iostream> // 用于錯誤輸出(如 cerr)
#include <string> // 用于字符串處理
核心類
ofstream: 輸出文件流,用于寫入文件(默認模式:ios::out)。
ifstream: 輸入文件流,用于讀取文件(默認模式:ios::in)。
fstream: 多功能文件流,支持讀寫(需手動指定模式)。
#include<iostream> // 輸入輸出流頭文件
#include <fstream> // 文件流操作頭文件
using namespace std; // 標準命名空間int main()
{// 1. 文件打開操作// 以二進制模式打開源文件(注意相對路徑的基準目錄)ifstream inf("../25011test/mystring.cpp", ios_base::binary);if(!inf){ // 文件打開失敗處理cout << "打開讀文件失敗" << endl;return 1; // 立即終止程序}// 以二進制模式創建/覆蓋目標文件ofstream outf("../25011test/test.cpp", ios_base::binary);if(!outf){ // 文件創建失敗處理cout << "打開寫文件失敗" << endl;return 1; // 立即終止程序}// 2. 文件讀寫操作char buf[32] = ""; // 固定緩沖區(存在溢出風險)inf.read(buf, 31); // 讀取最多31字節(預留結尾空字符)cout << buf << endl; // 輸出到控制臺(二進制數據可能亂碼)// 精確寫入實際讀取的字節數outf.write(buf, inf.gcount()); // gcount()獲取最后一次讀取的字節數// 3. 資源清理inf.close(); // 關閉輸入文件流(可省略,析構時自動關閉)outf.close(); // 關閉輸出文件流(同上)return 0; // 正常退出程序
}
練習: 實現文件復制
#include<iostream> // 輸入輸出流頭文件
#include <fstream> // 文件流操作頭文件
using namespace std; // 標準命名空間int main()
{// 1. 文件打開操作// 以二進制模式打開源文件(注意相對路徑的基準目錄)ifstream inf("../25011test/mystring.cpp", ios_base::binary);if(!inf){ // 文件打開失敗處理cout << "打開讀文件失敗" << endl;return 1; // 立即終止程序(新增錯誤處理)}// 以二進制模式創建/覆蓋目標文件ofstream outf("../25011test/test.cpp", ios_base::binary);if(!outf){ // 文件創建失敗處理cout << "打開寫文件失敗" << endl;return 1; // 立即終止程序(新增錯誤處理)}// 2. 文件讀寫操作char buf[32] = ""; // 固定緩沖區(32字節容量)while(true){ // 循環讀取直到文件結束inf.read(buf, 31); // 每次讀取31字節(預留空字符位)// 檢測實際讀取字節數(0表示到達文件末尾)if(inf.gcount() == 0) // gcount()獲取最后一次讀取的字節數break;outf.write(buf, inf.gcount()); // 精確寫入實際讀取的字節數}// 3. 資源清理(可省略,析構時會自動關閉)inf.close(); // 顯式關閉輸入文件流outf.close(); // 顯式關閉輸出文件流return 0; // 正常退出程序
}
總結
本文系統講解C++模板機制實現泛型編程的原理,涵蓋函數模板與類模板的語法特性及代碼實踐,深入剖析STL容器分類(順序容器、關聯容器)及其底層數據結構差異,通過vector、list、map等典型容器演示增刪查改操作,詳細解析異常處理流程包括標準異常與自定義異常實現,最后結合文件流操作示例展示二進制讀寫與安全處理方案,全面覆蓋C++高效編程核心知識點。