C++ 是一種廣泛使用的、面向?qū)ο蟮?、通用的編程語言,它在系統(tǒng)/應(yīng)用軟件開發(fā)、游戲開發(fā)、嵌入式系統(tǒng)等領(lǐng)域有著廣泛的應(yīng)用。對于初學(xué)者來說,掌握 C++ 的基礎(chǔ)知識和核心概念是學(xué)習(xí)編程的重要一步
1. C++ 的歷史與特點
C++ 由
Bjarne Stroustrup在 1985 年開發(fā),最初是作為 C 語言的擴(kuò)展,后來發(fā)展成為一種支持面向?qū)ο缶幊蹋?code>OOP)的語言。C++ 保留了 C 語言的大部分特性,同時增加了類、繼承、多態(tài)等OOP的核心概念,使得它在處理大型程序時更加高效和靈活
主要特點包括:
靈活性: C++ 支持多種編程范式,包括過程化編程、面向?qū)ο缶幊毯头盒途幊?。開發(fā)者可以根據(jù)需要選擇合適的編程風(fēng)格,使程序設(shè)計更加靈活和多樣化
面向?qū)ο缶幊蹋∣OP): C++ 支持類和對象的概念,通過封裝、繼承和多態(tài)等機制實現(xiàn)面向?qū)ο缶幊獭_@種編程方式提高了代碼的復(fù)用性和可維護(hù)性,使程序結(jié)構(gòu)更加清晰和模塊化
泛型編程: C++ 引入了模板機制,允許開發(fā)者編寫通用的代碼,從而提高代碼的復(fù)用性和靈活性。標(biāo)準(zhǔn)模板庫(
STL)提供了豐富的容器、算法和迭代器,簡化了常見的數(shù)據(jù)結(jié)構(gòu)和操作高性能: C++ 是一種編譯型語言,可以直接生成高效的機器碼,適合開發(fā)對性能要求極高的應(yīng)用。它繼承了 C 語言的高效特性,程序運行速度快,執(zhí)行效率高
內(nèi)存管理: C++ 提供了對內(nèi)存的直接控制,開發(fā)者可以手動管理內(nèi)存,但也需要謹(jǐn)慎處理以避免內(nèi)存泄漏等問題。C++ 還支持智能指針和 RAII 技術(shù),提高了內(nèi)存管理的安全性
可移植性: C++ 源代碼可以在不同平臺上編譯、鏈接和運行,無需為每個平臺單獨編寫代碼,節(jié)省時間和精力。C++ 的可移植性使其成為跨平臺開發(fā)的理想選擇
強類型檢查: C++ 是一種靜態(tài)類型語言,在編譯時進(jìn)行類型檢查,提高代碼的安全性和效率。這種類型檢查機制有助于在編譯階段發(fā)現(xiàn)潛在的錯誤,提高程序的健壯性
標(biāo)準(zhǔn)庫支持: C++ 標(biāo)準(zhǔn)庫(
Standard Template Library,STL)提供了大量的容器、算法和迭代器,方便開發(fā)者進(jìn)行高效的編程。這些庫函數(shù)涵蓋了從數(shù)據(jù)結(jié)構(gòu)到算法實現(xiàn)的各個方面,極大地提高了開發(fā)效率多線程支持: 自 C++11 起,C++ 引入了線程庫,便于多線程編程,實現(xiàn)并發(fā)執(zhí)行和任務(wù)分配。多線程支持使得 C++ 能夠更好地利用現(xiàn)代多核處理器的性能
兼容性: C++ 保持了與 C 語言的兼容性,絕大多數(shù) C 語言程序可以不經(jīng)修改直接在 C++ 環(huán)境中運行。這種兼容性使得 C++ 成為一種靈活的編程語言,能夠無縫集成現(xiàn)有的 C 語言代碼
指針: C++ 支持指針功能,可以直接與內(nèi)存交互,用于內(nèi)存、結(jié)構(gòu)和數(shù)組等。指針提供了對內(nèi)存的直接訪問能力,增強了程序的靈活性和效率
模塊化: C++ 允許使用函數(shù)將程序分解為部分,便于理解和修改。模塊化設(shè)計使得程序結(jié)構(gòu)更加清晰,便于維護(hù)和擴(kuò)展
2. C++ 的基本結(jié)構(gòu)
C++ 的基本結(jié)構(gòu)是構(gòu)成程序的基礎(chǔ),它決定了程序的組織方式和執(zhí)行流程
C++ 程序通常包含以下幾個部分
預(yù)處理指令: 如
#include <iostream>,用于引入頭文件
命名空間: 不同庫或模塊中相同標(biāo)識符(如變量、函數(shù)名)可通過命名空間隔離,命名空間類似于操作系統(tǒng)中的目錄和文件的關(guān)系,用于避免命名沖突,命名空間通過
namespace關(guān)鍵字定義,在命名空間外部,可以通過使用每個標(biāo)識符的完整名稱或using聲明來訪問其成員
1. using 聲明(引入特定成員)
2. using namespace 指令(引入整個空間,慎用)
3. 頭文件中禁止使用 using namespace,防止包含時擴(kuò)散沖突
4. 避免全局污染:優(yōu)先使用顯式限定(Namespace::member)或 using 聲明(非 using namespace)
#include <iostream>
using namespace std;
namespace First {
void sayHello() {
cout << "Hello First Namespace" << endl;
}
}
namespace Second {
void sayHello() {
cout << "Hello Second Namespace" << endl;
}
}
int main() {
First::sayHello();
Second::sayHello();
return 0;
}
嵌套命名空間
#include <iostream>
using namespace std;
namespace Outer {
int x = 10;
namespace Inner {
int y = 20;
}
}
int main() {
cout << Outer::x << endl;
cout << Outer::Inner::y << endl;
return 0;
}
主函數(shù):
int main()是程序的入口點
輸出語句: 如
std::cout << "Hello, World!";,用于輸出信息
返回值:
return 0;表示程序正常結(jié)束
#include <iostream>
int main() {
std::cout << "Hello, World!" << std::endl;
return 0;
}
輸出:Hello, World!
3. C++ 的基本語法
C++ 的語法與 C 語言非常相似,但有一些擴(kuò)展和改進(jìn)
C++ 的一些基本語法元素
1. 變量聲明
想要使用變量,必須先做
聲明,同時還要指明保存數(shù)據(jù)所需要的空間大小,使用int,float,bool,char等關(guān)鍵字聲明變量
int a; // 聲明
a = 1; // 賦值操作
int a = 1; // 聲明且賦值操作
a = 2; // 修改變量值
2. 數(shù)據(jù)類型
C++ 提供了多種基本數(shù)據(jù)類型,如
int,float,double,bool,char等,使用sizeof()運算符可獲取類型或變量占用的字節(jié)數(shù)
一、基本數(shù)據(jù)類型
整數(shù)類型: 用于表示整數(shù)值,包括
int、short、long、long long以及它們的無符號版本unsigned int、unsigned short、unsigned long、unsigned long long。這些類型用于存儲整數(shù)數(shù)據(jù),其范圍和精度取決于具體類型。例如,int通常占用 4 個字節(jié),而short通常占用 2 個字節(jié)浮點類型: 用于表示實數(shù),包括
float、double和long double。float通常占用 4 個字節(jié),可以存儲 7 位小數(shù);double通常占用 8 個字節(jié),可以存儲 15 位小數(shù);long double通常占用 16 個字節(jié),精度更高字符類型: 用于表示單個字符,包括
char和wchar_t。char通常占用 1 個字節(jié),而wchar_t用于表示寬字符,但其大小由實現(xiàn)定義,不可靠布爾類型: 用于表示布爾值,即
true或false,通常占用 1 個字節(jié)空類型: 表示沒有值或沒有返回值,常用于函數(shù)返回值或指針,如
void*
二、用戶自定義數(shù)據(jù)類型
結(jié)構(gòu)體(struct)
結(jié)構(gòu)體用于封裝多個不同類型的成員變量,從而形成一個邏輯上相關(guān)的數(shù)據(jù)集合,C++中
struct可包含構(gòu)造函數(shù)、析構(gòu)函數(shù)、繼承和多態(tài),功能與class幾乎相同,僅默認(rèn)權(quán)限不同,結(jié)構(gòu)體成員默認(rèn)為公有(public),結(jié)構(gòu)體的成員可以通過點運算符(.)
struct 結(jié)構(gòu)體名稱 {
數(shù)據(jù)類型 成員1;
數(shù)據(jù)類型 成員2;
// ...
};
struct Student {
// 數(shù)據(jù)成員
int id;
std::string name;
double score;
Student(int a, std::string b, double c) : id(a), name(b), score(c), {} // 自定義構(gòu)造函數(shù)
// 成員函數(shù)(C++支持)
void printInfo() {
std::cout << "ID: " << id << ", Name: " << name << std::endl;
}
};
Student p1(10, "Alice", 1.5);
std::cout << "ID: " << p1.id << ", Name: " << p1.name << ", Score: " << p1.score << std::endl;
// 2. 創(chuàng)建一個學(xué)生對象
// 注意:這里使用了一個簡單的構(gòu)造函數(shù)來初始化學(xué)生對象
Student s1 = {101, "Alice", 95.5}; // C++11起支持
std::cout << "ID: " << s1.id << ", Name: " << s1.name << ", Score: " << s1.score << std::endl;
輸出:
ID: 10, Name: Alice, Score: 1.5
ID: 101, Name: Alice, Score: 95.5
聯(lián)合(union)
聯(lián)合用于存儲多個數(shù)據(jù)元素,但這些元素共享同一內(nèi)存位置,因此只能同時存儲一個元素
聯(lián)合的大小等于其成員中最大的成員的大小
聯(lián)合不能作為基類使用,因為它不能繼承自其他類
聯(lián)合不能包含引用類型的成員,因為引用需要額外的存儲空間
匿名聯(lián)合不使用點運算符,其成員必須是數(shù)據(jù)類型,不允許有成員函數(shù)或私有/受保護(hù)的成員,為了防止訪問錯誤
建議在聯(lián)合中定義一個額外的對象(判別式)來跟蹤當(dāng)前存儲的值的類型
#include <iostream>
using namespace std;
class Token {
public:
enum TokenKind { INT, CHAR, DBL };
TokenKind tok;
union {
char cval;
int ival;
double dval;
} val;
void print() {
switch (tok) {
case INT:
cout << "Integer: " << val.ival << endl;
break;
case CHAR:
cout << "Character: " << val.cval << endl;
break;
case DBL:
cout << "Double: " << val.dval << endl;
break;
}
}
};
int main() {
Token token;
token.tok = Token::INT;
token.val.ival = 42;
token.print();
token.tok = Token::CHAR;
token.val.cval = 'a';
token.print();
token.tok = Token::DBL;
token.val.dval = 3.1416;
token.print();
return 0;
}
枚舉(enum)
枚舉用于定義一組命名的常量,枚舉成員默認(rèn)從 0 開始遞增,也可以顯式指定值
enum class WeekDay {
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
};
WeekDay today = WeekDay::Wednesday; // 成員需通過枚舉名::訪問,避免命名沖突
enum RGB {
R = 2,
G,
B = 5
};
G的值為R+1,結(jié)果為3
類(class)
類用于創(chuàng)建類實例,包含成員函數(shù)和數(shù)據(jù)成員,是 C++ 面向?qū)ο缶幊痰暮诵?/p>
class ClassName {
public: // 公共成員(外部可訪問)
// 構(gòu)造函數(shù)
ClassName(參數(shù)列表) { ... }
// 成員函數(shù)
void memberFunc() { ... }
protected: // 受保護(hù)成員(僅子類及自身可訪問)
int protectedVar;
private: // 私有成員(僅自身可訪問)
int privateVar;
}; // 注意分號不可省略
// 類定義
class Person {
public:
void setName(const std::string& name);
std::string getName() const;
private:
std::string name;
};
// 成員函數(shù)的實現(xiàn)
void Person::setName(const std::string& name) {
this->name = name;
}
std::string Person::getName() const {
return name;
}
三、派生數(shù)據(jù)類型
派生數(shù)據(jù)類型是從基本數(shù)據(jù)類型或用戶定義數(shù)據(jù)類型衍生出來的數(shù)據(jù)類型
數(shù)組
數(shù)組用于存儲固定大小、相同類型元素的連續(xù)內(nèi)存集合,編譯時確定大小,不可動態(tài)擴(kuò)展,數(shù)組索引從 0 開始,支持多維數(shù)組,內(nèi)存連續(xù)性:元素物理地址相鄰,支持高效隨機訪問
聲明方式
// 一維數(shù)組
int arr1[5](); // 未初始化,值為隨機數(shù)
int arr2[5]() = {1, 2}; // 部分初始化,剩余元素自動補0 → {1,2,0,0,0}
int arr3[] = {1,2,3}; // 自動推斷大小為3
// 二維數(shù)組(數(shù)組的數(shù)組)
int matrix[2][3] = {{1,2,3}, {4,5,6}}; // 標(biāo)準(zhǔn)初始化
int matrix2[][2] = {1,2,3,4}; // 省略行數(shù),自動推斷為2行
遍歷方式
int arr[5]() = {10,20,30,40,50};
// 1. for循環(huán)(推薦控制索引范圍)
for(int i=0; i<5; i++) cout << arr[i] << " ";
// 2. 范圍for循環(huán)(C++11)
for(int num : arr) cout << num << " ";
// 3. 指針遍歷
int *p = arr;
while(p < arr + 5) cout << *p++ << " ";
多維數(shù)組操作
int matrix[2]()[3]() = {{1,2,3}, {4,5,6}};
// 嵌套循環(huán)遍歷
for(int i=0; i<2; i++) {
for(int j=0; j<3; j++) {
cout << matrix[i][j] << " ";
}
}
// 內(nèi)存按行連續(xù)存儲:matrix[0][3] 等價于 matrix[1]()[0]()
指針
指針用于存儲變量的地址,它允許程序員直接訪問和操作內(nèi)存地址,通過指針,可以實現(xiàn)對內(nèi)存的靈活管理、動態(tài)分配、數(shù)組操作、函數(shù)調(diào)用等高級功能
獲取地址與解引用
int num = 10;
int *ptr = # // ptr 指向 num 的地址
cout << *ptr; // 輸出 10
空指針與野指針
空指針(null pointer)通常用 nullptr(C++11 引入)或 NULL 表示,表示沒有指向任何有效內(nèi)存地址。野指針(dangling pointer)是指指向已釋放內(nèi)存的指針,訪問野指針會導(dǎo)致未定義行為,預(yù)防:指針釋放后置為NULL(delete p; p = NULL;),避免再次使用
動態(tài)內(nèi)存分配
int *pn = new int; // 分配一個整數(shù)的內(nèi)存
int *psome = new int[20]; // 分配 20 個整數(shù)的內(nèi)存
delete pn; // 使用 delete 釋放內(nèi)存
delete[] psome;
指針運算
指針可以進(jìn)行算術(shù)運算,如加減操作,但需注意基于指針指向的數(shù)據(jù)類型大小
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;
p++; // 指向下一個元素
指針與數(shù)組
數(shù)組名在大多數(shù)情況下被視為指向數(shù)組首元素的指針,但數(shù)組和指針本質(zhì)不同。數(shù)組名沒有自己的存儲空間,而指針有自己的存儲空間
指針作為函數(shù)參數(shù)
將指針作為函數(shù)參數(shù)傳遞時,函數(shù)內(nèi)對指針?biāo)赶蜃兞康男薷膶⒂绊懺甲兞?/p>
void increment(int *p) {
(*p)++;
}
int num = 10;
increment(&num); // num 變?yōu)?11
智能指針
智能指針是C++標(biāo)準(zhǔn)庫(
<memory>頭文件)提供的自動內(nèi)存管理工具,通過RAII(資源獲取即初始化)機制,將動態(tài)分配的內(nèi)存(堆內(nèi)存)封裝為對象,在智能指針生命周期結(jié)束時自動釋放所管理的內(nèi)存,C++智能指針是現(xiàn)代C++內(nèi)存管理的核心工具,正確使用它們可以大幅提升代碼的安全性和可維護(hù)性,解決了手動管理內(nèi)存的兩大痛點:
內(nèi)存泄漏(忘記delete)
懸空指針(對象已釋放但指針仍被使用)
unique_ptr: 用于獨占所有權(quán)的場景(如管理局部動態(tài)對象、避免拷貝)
shared_ptr: 用于共享所有權(quán)的場景(如多個對象共享同一資源)
weak_ptr: 用于解決
shared_ptr循環(huán)引用的問題(如觀察者模式、緩存)
創(chuàng)建
unique_ptr: 獨占所有權(quán)的智能指針,同一時間只能有一個
unique_ptr指向同一個對象,禁止拷貝(copy constructor和operator被刪除),但允許移動(move constructor和move operator有效,優(yōu)先使用std::make_unique(C++14引入),它更簡潔、異常安全(避免new后構(gòu)造函數(shù)拋出異常導(dǎo)致內(nèi)存泄漏),當(dāng)unique_ptr超出作用域或被reset()時,自動調(diào)用delete釋放所管理的內(nèi)存(若管理數(shù)組,則調(diào)用delete[])
#include <memory>
#include <iostream>
class Test {
public:
Test(int id) : id_(id) { std::cout << "Test " << id_ << " constructed.\n"; }
~Test() { std::cout << "Test " << id_ << " destructed.\n"; }
void show() { std::cout << "Test " << id_ << " is alive.\n"; }
private:
int id_;
};
int main() {
// 方式1:用make_unique創(chuàng)建(推薦)
std::unique_ptr<Test> up1 = std::make_unique<Test>(1);
up1->show(); // 訪問對象成員
// 方式2:用new直接構(gòu)造(不推薦,除非需要自定義刪除器)
std::unique_ptr<Test> up2(new Test(2));
up2->show();
return; // up1、up2超出作用域,自動析構(gòu)Test對象
}
輸出:
Test 1 constructed
Test 1 is alive
Test 2 constructed
Test 2 is alive
Test 2 destructed
Test 1 destructed
shared_ptr: 共享所有權(quán)的智能指針,多個
shared_ptr可以指向同一個對象,通過引用計數(shù)(reference count)跟蹤對象的被引用次數(shù),當(dāng)最后一個shared_ptr析構(gòu)或reset()時,引用計數(shù)變?yōu)?,自動釋放對象內(nèi)存,引用計數(shù)的修改是原子操作(線程安全),但對象本身的訪問需要手動同步(如用mutex)
#include <memory>
#include <iostream>
class Test {
public:
Test(int id) : id_(id) { std::cout << "Test " << id_ << " constructed.\n"; }
~Test() { std::cout << "Test " << id_ << " destructed.\n"; }
void show() { std::cout << "Test " << id_ << " is alive.\n"; }
private:
int id_;
};
int main() {
// 方式1:用make_shared創(chuàng)建(推薦)
std::shared_ptr<Test> sp1 = std::make_shared<Test>(1);
std::cout << "sp1 use count: " << sp1.use_count() << "\n"; // 引用計數(shù):1
// 方式2:用new直接構(gòu)造(不推薦)
std::shared_ptr<Test> sp2(new Test(2));
std::cout << "sp2 use count: " << sp2.use_count() << "\n"; // 引用計數(shù):1
// 拷貝shared_ptr,引用計數(shù)增加
std::shared_ptr<Test> sp3 = sp1;
std::cout << "sp1 use count after copy: " << sp1.use_count() << "\n"; // 引用計數(shù):2
return; // sp1、sp2、sp3析構(gòu),引用計數(shù)變?yōu)?,自動釋放對象
}
輸出:
Test 1 constructed
sp1 use count: 1
Test 2 constructed
sp2 use count: 1
sp1 use count after copy: 2
Test 2 destructed
Test 1 destructed
移動所有權(quán)
unique_ptr: 不能拷貝,但可以通過
std::move轉(zhuǎn)移所有權(quán)(原unique_ptr變?yōu)?nullptr)
std::unique_ptr<Test> up1 = std::make_unique<Test>(1);
std::unique_ptr<Test> up2 = std::move(up1); // 轉(zhuǎn)移所有權(quán),up1變?yōu)閚ullptr
if (up1 == nullptr) {
std::cout << "up1 is null.\n";
}
up2->show(); // up2擁有對象所有權(quán)
輸出:
Test 1 constructed
up1 is null
Test 1 is alive
Test 1 destructed
管理動態(tài)數(shù)組
unique_ptr: 支持管理動態(tài)數(shù)組,需指定數(shù)組類型(
T[]),此時會自動調(diào)用delete[]釋放內(nèi)存
std::unique_ptr<int[]> up_arr = std::make_unique<int[]>(5); // 創(chuàng)建5個int的數(shù)組
for (int i = 0; i < 5; ++i) {
up_arr[i] = i + 1; // 像普通數(shù)組一樣訪問
std::cout << up_arr[i] << " ";
}
std::cout << "\n";
// 超出作用域時,自動釋放數(shù)組(delete[])
引用計數(shù)操作
shared_ptr:
use_count():返回當(dāng)前引用計數(shù)(僅用于調(diào)試,不要依賴其值做邏輯判斷)
reset():重置shared_ptr,引用計數(shù)減少(若指定新對象,則指向新對象)
get():返回裸指針(需謹(jǐn)慎使用,避免懸空指針)
std::shared_ptr<Test> sp1 = std::make_shared<Test>(1);
std::shared_ptr<Test> sp2 = sp1;
std::cout << "sp1 use count: " << sp1.use_count() << "\n"; // 2
sp2.reset(); // sp2重置,引用計數(shù)減少到1
std::cout << "sp1 use count after reset sp2: " << sp1.use_count() << "\n"; // 1
sp1.reset(new Test(2)); // sp1指向新對象,原對象引用計數(shù)變?yōu)?,自動釋放
std::cout << "sp1 use count after reset to new object: " << sp1.use_count() << "\n"; // 1
輸出:
Test 1 constructed
sp1 use count: 2
sp1 use count after reset sp2: 1
Test 1 destructed
Test 2 constructed
sp1 use count after reset to new object: 1
Test 2 destructed
循環(huán)引用問題
shared_ptr: 當(dāng)兩個
shared_ptr互相引用時,會導(dǎo)致引用計數(shù)永遠(yuǎn)不為0,內(nèi)存無法釋放(內(nèi)存泄漏)
#include <memory>
#include <iostream>
struct A {
std::shared_ptr<B> b_ptr;
~A() { std::cout << "A destructed.\n"; }
};
struct B {
std::shared_ptr<A> a_ptr;
~B() { std::cout << "B destructed.\n"; }
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->b_ptr = b; // A引用B
b->a_ptr = a; // B引用A(循環(huán)引用)
// a和b析構(gòu)時,引用計數(shù)各為1(互相引用),無法釋放
return;
}
輸出:
(無析構(gòu)輸出,內(nèi)存泄漏)
避免循環(huán)引用: 用
weak_ptr代替shared_ptr來打破循環(huán)不要用裸指針初始化多個shared_ptr:
int* p = new int;
std::shared_ptr<int> sp1(p);
std::shared_ptr<int> sp2(p);
會導(dǎo)致p被釋放兩次(程序崩潰)
優(yōu)先使用make_shared:
make_shared比new更高效,且避免內(nèi)存泄漏(例如new后構(gòu)造函數(shù)拋出異常,make_shared會自動回收內(nèi)存
弱引用的智能指針
弱引用: 指向
shared_ptr管理的對象,但不增加引用計數(shù)(不影響對象的生命周期)解決循環(huán)引用: 當(dāng)兩個對象互相引用時,用
weak_ptr代替其中一個shared_ptr,打破循環(huán)需鎖定訪問: 不能直接訪問對象,必須通過
lock()方法獲取shared_ptr(若對象已釋放,lock()返回nullptr)
修改上面的循環(huán)引用例子,將j結(jié)構(gòu)體 B 中的 shared_ptr<A> 改為 weak_ptr<A>
#include <memory>
#include <iostream>
struct A {
std::shared_ptr<B> b_ptr;
~A() { std::cout << "A destructed.\n"; }
};
struct B {
std::weak_ptr<A> a_ptr; // 用weak_ptr代替shared_ptr
~B() { std::cout << "B destructed.\n"; }
};
int main() {
std::shared_ptr<A> a = std::make_shared<A>();
std::shared_ptr<B> b = std::make_shared<B>();
a->b_ptr = b; // A引用B(shared_ptr)
b->a_ptr = a; // B引用A(weak_ptr,不增加引用計數(shù))
// a析構(gòu)時,引用計數(shù)變?yōu)?(b的a_ptr是weak_ptr,不影響),釋放A;
// A釋放后,b的引用計數(shù)變?yōu)?(a的b_ptr是shared_ptr),釋放B。
return;
}
輸出:
A destructed
B destructed
訪問 weak_ptr 指向的對象: 需通過
lock()方法獲取shared_ptr,若對象已釋放,lock()返回nullptr
std::shared_ptr<Test> sp = std::make_shared<Test>(1);
std::weak_ptr<Test> wp = sp; // 從shared_ptr構(gòu)造weak_ptr
// 方式1:用lock()獲取shared_ptr
if (auto locked_sp = wp.lock()) {
locked_sp->show(); // 訪問對象
} else {
std::cout << "Object has been destructed.\n";
}
// 方式2:用expired()判斷對象是否已釋放
if (!wp.expired()) {
auto locked_sp = wp.lock();
locked_sp->show();
} else {
std::cout << "Object has been destructed.\n";
}
sp.reset(); // 釋放對象
if (wp.expired()) {
std::cout << "Object has been destructed.\n";
}
輸出:
Test 1 constructed
Test 1 is alive
Test 1 is alive
Test 1 destructed
Object has been destructed
函數(shù)
函數(shù)包含邏輯代碼,可被程序多次調(diào)用,一個完整的函數(shù)定義主要包括:返回類型、函數(shù)名、參數(shù)列表和函數(shù)體
返回類型 函數(shù)名(參數(shù)列表) {
// 函數(shù)體
}
返回類型: 指定返回值的數(shù)據(jù)類型(如
void表示無返回值)函數(shù)名稱: 這是函數(shù)的實際名稱。函數(shù)名和參數(shù)列表一起構(gòu)成了函數(shù)簽名(告知編譯器)
參數(shù)列表: 參數(shù)就像是占位符,可為空或包含多個參數(shù),參數(shù)按值傳遞(拷貝副本)或引用傳遞(修改原始數(shù)據(jù))
函數(shù)體: 函數(shù)要執(zhí)行的語句塊,函數(shù)的主體
高級特性
函數(shù)重載: 是 C++ 中編譯時多態(tài)的核心機制之一,重載函數(shù)的聲明和定義與普通函數(shù)類似,但需保證參數(shù)列表不同,通過不同參數(shù)列表實現(xiàn)同名函數(shù),需滿足參數(shù)類型、個數(shù)或順序差異
1. 參數(shù)個數(shù)不同
void fun()
void fun(int a)
2. 參數(shù)類型不同
void fun(int a)
void fun(double a)
3. 參數(shù)順序不同: 不同順序中需要有不同的類型
void fun(int a, double b)
void fun(double a, int b)
void fun(int a, int b)
void fun(int b int a)
// 這種是不行的, 形參名的調(diào)換不構(gòu)成重載
注意:返回值類型不同不能作為函數(shù)重載的依據(jù)
// 無法重載,編譯器會報錯“重定義”
int fun()
void fun()
引用參數(shù)的特殊處理
當(dāng)重載函數(shù)包含引用參數(shù)(
int&、const int&、int&&)時,編譯器會根據(jù)實參的左值/右值屬性匹配
1. int&: 匹配可修改的左值(如變量)
2. const int&: 匹配不可修改的左值(如 const 變量)或右值(如表達(dá)式結(jié)果)
3. int&&: 匹配右值(如臨時對象、表達(dá)式結(jié)果)
void fun(int& a) { cout << "左值引用: " << a << endl; }
void fun(const int& a) { cout << "const左值引用: " << a << endl; }
void fun(int&& a) { cout << "右值引用: " << a << endl; }
int main() {
int a = 10; // 左值
const int b = 20; // const左值
fun(a); // 調(diào)用 fun(int&)
fun(b); // 調(diào)用 fun(const int&)
fun(a + b); // 調(diào)用 fun(int&&)(a+b是右值)
return 0;
}
輸出:
左值引用: 10
const左值引用: 20
右值引用: 30
函數(shù)重載僅在同一作用域內(nèi)有效:
void fun() { cout << "全局函數(shù)" << endl; } // 全局作用域
class MyClass {
public:
void fun() { cout << "類成員函數(shù)" << endl; } // 類作用域
};
int main() {
fun(); // 調(diào)用全局函數(shù)
MyClass obj;
obj.fun(); // 調(diào)用類成員函數(shù)
return 0;
}
上述代碼不會報錯,因為兩個fun函數(shù)屬于不同作用域
默認(rèn)參數(shù)
在聲明或定義中為參數(shù)指定默認(rèn)值,調(diào)用時可省略
void display(int a = 10, int b = 20) { /* ... */ }
display();
內(nèi)聯(lián)函數(shù)
使用
inline關(guān)鍵字建議編譯器展開函數(shù)體,也就是代碼直接嵌入調(diào)用處,減少調(diào)用開銷
適用于: (通常 ≤ 10行代碼)且頻繁調(diào)用(如循環(huán)內(nèi)部)、類成員訪問器( getter/setter )
不適用于: 函數(shù)體含循環(huán)/遞歸、代碼較長(導(dǎo)致代碼膨脹)
// 正確方式:定義時加 inline 關(guān)鍵字(聲明中加無效)
inline int add(int a, int b) {
return a + b;
}
// 類內(nèi)定義自動內(nèi)聯(lián)(無需顯式寫 inline)
class Circle {
private:
double radius;
public:
double getArea() { // 自動視為內(nèi)聯(lián)
return 3.14 * radius * radius;
}
};
// 類外定義需顯式 inline
class Circle {
public:
double getArea();
};
inline double Circle::getArea() {
return 3.14 * radius * radius;
}
遞歸函數(shù)
函數(shù)直接或間接調(diào)用自身,需設(shè)置基準(zhǔn)條件避免無限遞歸
int factorial(int n) {
return n == 1 ? 1 : n * factorial(n-1);
}
Lambda 函數(shù)與表達(dá)式
C++11 提供了對匿名函數(shù)的支持,稱為 Lambda 函數(shù)(也叫 Lambda 表達(dá)式),它們通常用于需要一個小型、臨時的函數(shù),以便在某個上下文中使用
[capture_list](parameter_list) mutable -> return_type { function_body; }
捕獲列表 (capture_list): 指定哪些外部變量可以被 Lambda 函數(shù)訪問,以及是以值還是引用的方式訪問
mutable:允許修改值捕獲的副本(默認(rèn)不可修改)
#include <iostream>
int main() {
int x = 10;
// 使用 mutable 關(guān)鍵字允許修改按值捕獲的變量
auto lambda = [x]() mutable {
x += 5;
std::cout << "x inside lambda: " << x << std::endl; // 輸出: x inside lambda: 15
};
lambda();
std::cout << "x outside lambda: " << x << std::endl; // 輸出: x outside lambda: 10
return 0;
}
常見的捕獲方式包括
[]: 不捕獲任何變量,使用未定義變量會引發(fā)錯誤
[&]: 捕獲所有外部變量的引用,任何被使用到的外部變量都隱式地以引用方式加以引用
[=]: 捕獲所有外部變量的值(副本),任何被使用到的外部變量都隱式地以傳值方式加以引用
[a, &b]: 變量 a 按值捕獲,變量 b 按引用捕獲
[=, &a, &b]: 除 a 和 b 按引用捕獲外,其他變量按值捕獲
[&, a, b]: 除 a 和 b 按值捕獲外,其他變量按引用捕獲
[this]: 捕獲指向當(dāng)前對象的 this 指針(訪問成員變量)
#include <iostream>
int main() {
int x = 10;
int y = 5;
// 按值捕獲 x,按引用捕獲 y
auto lambda = [=, &y]() -> int {
x += 2;
y += 3;
return x + y;
};
int result = lambda();
std::cout << "Result: " << result << std::endl; // 輸出: Result: 20
std::cout << "x: " << x << ", y: " << y << std::endl; // 輸出: x: 10, y: 8
return 0;
}
參數(shù)列表 (parameter_list): Lambda 函數(shù)的參數(shù)列表,類似于普通函數(shù)
返回類型 (return_type): Lambda 函數(shù)的返回類型,可以省略,編譯器會自動推斷
函數(shù)體 (function_body): Lambda 函數(shù)的具體實現(xiàn)代碼
虛函數(shù)和純虛函數(shù)
虛函數(shù)允許子類重寫父類的方法。純虛函數(shù)是一種沒有實現(xiàn)的方法,要求子類必須實現(xiàn)
class Base {
public:
virtual void display() = 0; // 純虛函數(shù)
};
class Derived : public Base {
public:
void display() override {
std::cout << "Derived class" << std::endl;
}
};
友元函數(shù)
友元函數(shù)是一種非成員函數(shù),但可以訪問其私有和受保護(hù)的成員。友元函數(shù)通過在類內(nèi)部使用
friend關(guān)鍵字聲明,從而獲得訪問類私有成員的權(quán)限。友元函數(shù)不是類的成員函數(shù),因此不能使用this指針
class Box {
private:
int width;
public:
Box(int w) : width(w) {}
friend int friendGetArea(Box box);
};
int friendGetArea(Box box) {
return box.width * box.width;
}
int main() {
Box box(5);
std::cout << friendGetArea(box) << std::endl; // 輸出: Area: 25
}
構(gòu)造函數(shù)和拷貝構(gòu)造函數(shù)
構(gòu)造函數(shù)在創(chuàng)建對象時自動調(diào)用,用于初始化資源和狀態(tài)??截悩?gòu)造函數(shù)用于從現(xiàn)有對象復(fù)制數(shù)據(jù)到新對象
class A {
public:
A() {} // 構(gòu)造函數(shù)
A(const A& other) {} // 拷貝構(gòu)造函數(shù)
};
引用
引用是C++中的一種特殊的數(shù)據(jù)類型描述,用于在程序的不同部分使用兩個以上的變量名指向同一塊地址,使得對其中任何一個變量的操作實際上都是對同一地址單元進(jìn)行的操作。引用的聲明方式為:類型名稱
&引用名=原名
int a = 10;
int& b = a; // b是a的引用
b = 20; // 修改b會同步修改a
cout << a; // 輸出20 [3]()
引用的核心特點
1. 必須初始化: 聲明時必須綁定目標(biāo)變量,如 int& b = a
2. 無獨立內(nèi)存空間: 引用與目標(biāo)變量共享內(nèi)存地址,&b == &a
3. 不可重新綁定: 一旦綁定目標(biāo)變量,無法再指向其他變量
4. 安全性優(yōu)勢: 不能為 NULL,避免空指針風(fēng)險
5. 引用作為函數(shù)參數(shù): 使用引用傳遞參數(shù)能避免數(shù)據(jù)副本,提高效率,且更清晰
#include <iostream>
using namespace std;
void exchange(int& x, int& y) {
int temp = x;
x = y;
y = temp;
}
int main() {
int a, b;
cout << "請輸入兩個數(shù)字: " << endl;
cin >> a >> b;
exchange(a, b);
cout << "交換后: " << a << " " << b << endl;
return 0;
}
常量
常量是固定且不可變的值,一旦初始化,其值在程序中不可改變。常量通常使用
const關(guān)鍵字修飾,常量的作用域與其他變量相同,可以是塊作用域、函數(shù)作用域、文件作用域或命名空間作用域
void example() {
const int BLOCK_CONSTANT = 5; // 塊作用域
// BLOCK_CONSTANT 只能在 example 函數(shù)內(nèi)部使用
}
const int GLOBAL_CONSTANT = 10; // 文件作用域
// GLOBAL_CONSTANT 可以在整個文件中使用
常量可以是任何基本數(shù)據(jù)類型或復(fù)合數(shù)據(jù)類型,包括指針(但指針指向的內(nèi)容可以被修改,除非指針本身也是指向常量的指針)
const int* ptr; // 指針指向的值是常量
int* const ptr2; // 指針本身是常量
const int* const ptr3; // 指針本身和指向的值都是常量
常量引用可以初始化為左值或右值,但初始化為右值時,只能使用
const T&類型
int a = 10;
const int& ref = a; // 正確
const int& ref2 = 20; // 正確
int& ref3 = 30; // 錯誤
常量函數(shù): 通過在函數(shù)名后添加
const關(guān)鍵字聲明為常量函數(shù),這些函數(shù)不能改變對象的值,且只能由非常量對象調(diào)用
class Example {
public:
int getValue() const {
return value;
}
private:
int value;
};
四、 運算符
包括算術(shù)運算符(+, -, *, /, %)、關(guān)系運算符(==, !=, >, <, >=, <=)、邏輯運算符(&&, ||, !)等
算術(shù)運算符
#include <iostream>
using namespace std;
int main() {
int a = 10, b = 20;
cout << "加法: " << a + b << endl; // 輸出: 30
cout << "減法: " << a - b << endl; // 輸出: -10
cout << "乘法: " << a * b << endl; // 輸出: 200
cout << "除法: " << b / a << endl; // 輸出: 2
cout << "取模: " << b % a << endl; // 輸出: 0
return 0;
}
關(guān)系運算符
#include <iostream>
using namespace std;
int main() {
int a = 10, b = 20;
cout << "等于: " << (a == b) << endl; // 輸出: 0 (false)
cout << "不等于: " << (a != b) << endl; // 輸出: 1 (true)
cout << "大于: " << (a > b) << endl; // 輸出: 0 (false)
cout << "小于: " << (a < b) << endl; // 輸出: 1 (true)
cout << "大于等于: " << (a >= b) << endl; // 輸出: 0 (false)
cout << "小于等于: " << (a <= b) << endl; // 輸出: 1 (true)
return 0;
}
邏輯運算符
#include <iostream>
using namespace std;
int main() {
bool x = true, y = false;
cout << "邏輯與: " << (x && y) << endl; // 輸出: 0 (false)
cout << "邏輯或: " << (x || y) << endl; // 輸出: 1 (true)
cout << "邏輯非: " << (!x) << endl; // 輸出: 0 (false)
return 0;
}
賦值運算符
#include <iostream>
using namespace std;
int main() {
int a = 10;
int b = a; // 基本賦值
int c = 20;
c += a; // 加法賦值,等價于 c = c + a
c -= a; // 減法賦值,等價于 c = c - a
c *= a; // 乘法賦值,等價于 c = c * a
c /= a; // 除法賦值,等價于 c = c / a
c %= a; // 取模賦值,等價于 c = c % a
cout << "基本賦值: " << b << endl; // 輸出: 10
cout << "加法賦值: " << c << endl; // 輸出: 30
cout << "減法賦值: " << c << endl; // 輸出: 20
cout << "乘法賦值: " << c << endl; // 輸出: 200
cout << "除法賦值: " << c << endl; // 輸出: 20
cout << "取模賦值: " << c << endl; // 輸出: 0
return 0;
}
指針運算符
#include <iostream>
using namespace std;
int main() {
int var = 20;
int *ptr = &var; // 取地址
cout << "變量的地址: " << ptr << endl;
cout << "通過指針訪問變量的值: " << *ptr << endl; // 解引用
return 0;
}
位運算符
#include <iostream>
using namespace std;
int main() {
int a = 60; // 二進(jìn)制表示為 0011 1100
int b = 13; // 二進(jìn)制表示為 0000 1101
cout << "按位與: " << (a & b) << endl; // 輸出: 12 (二進(jìn)制 0000 1100)
cout << "按位或: " << (a | b) << endl; // 輸出: 61 (二進(jìn)制 0011 1101)
cout << "按位異或: " << (a ^ b) << endl; // 輸出: 49 (二進(jìn)制 0011 0001)
cout << "左移: " << (a << 2) << endl; // 輸出: 240 (二進(jìn)制 1111 0000)
cout << "右移: " << (a >> 2) << endl; // 輸出: 15 (二進(jìn)制 0000 1111)
return 0;
}
五、控制結(jié)構(gòu)
包括
if-else、switch、for、while等循環(huán)和條件語句
if (x > 0) {
cout << "Positive number";
} else if (x == 0) {
cout << "Zero";
} else {
cout << "Negative number";
}
#include <iostream>
using namespace std;
int main() {
char op;
float num1, num2;
cout << "Enter operator either + or - or * or /: ";
cin >> op;
cout << "Enter two operands: ";
cin >> num1 >> num2;
switch(op) {
case '+':
cout << num1 + num2;
break;
case '-':
cout << num1 - num2;
break;
case '*':
cout << num1 * num2;
break;
case '/':
if(num2 != 0.0)
cout << num1 / num2;
else
cout << "Divide by zero situation";
break;
default:
cout << "Error! operator is not correct";
break;
}
return 0;
}
for (int i = 0; i < 10; i++) {
if (i == 5) break;
cout << i << " ";
}
int i = 0;
while (i < 10) {
cout << i << " ";
i++;
}
int i = 0;
do {
cout << i << " ";
i++;
} while (i < 10);
4. 面向?qū)ο缶幊蹋∣OP)
C++ 是一種面向?qū)ο蟮木幊陶Z言,支持
OOP的核心概念
1. 類(Class)
定義對象的藍(lán)圖,包含數(shù)據(jù)成員和成員函數(shù)
2. 對象(Object)
類的實例,具有類定義的屬性和行為
3. 封裝(Encapsulation)
通過訪問修飾符(
public,private,protected)控制類的內(nèi)部成員的訪問
4. 繼承(Inheritance)
子類可以繼承父類的屬性和方法,實現(xiàn)代碼復(fù)用
5. 多態(tài)(Polymorphism)
通過虛函數(shù)實現(xiàn)不同類對象的統(tǒng)一接口,提高代碼的靈活性
#include <iostream>
class Rectangle {
public:
int width, height;
Rectangle(int w, int h) : width(w), height(h) {}
int area() const {
return width * height;
}
};
int main() {
Rectangle rect(5, 10);
std::cout << "Area: " << rect.area() << std::endl;
return 0;
}
輸出:Area: 50
5. 標(biāo)準(zhǔn)模板庫(STL)
C++ 的
STL提供了豐富的容器、算法和迭代器,簡化了開發(fā)過程
容器
管理數(shù)據(jù)的集合,封裝存儲結(jié)構(gòu)
常用的容器包括
向量(vector): 動態(tài)數(shù)組,支持隨機訪問
push_back(value):在向量末尾添加一個元素
pop_back():移除向量的最后一個元素
at(index):返回指定索引位置的元素,如果索引越界會拋出異常
operator[]:返回指定索引位置的元素,不進(jìn)行越界檢查
front():返回向量的第一個元素
back():返回向量的最后一個元素
begin():返回指向向量第一個元素的迭代器
end():返回指向向量末尾元素后一個位置的迭代器
size():返回向量中元素的數(shù)量
capacity():返回當(dāng)前向量分配的內(nèi)存大小
resize(new_size, value):調(diào)整向量大小,如果新大小大于當(dāng)前大小,則用指定值填充新元素;如果新大小小于當(dāng)前大小,則刪除多余元素
reserve(new_capacity):預(yù)先分配足夠的內(nèi)存空間,避免頻繁的內(nèi)存重新分配
clear():清空向量中的所有元素
empty():判斷向量是否為空
swap(other_vector):交換兩個向量的內(nèi)容
insert(position, value):在指定位置插入一個元素
erase(position):刪除指定位置的元素
sort():對向量中的元素進(jìn)行排序
#include <iostream>
#include <vector>
#include <algorithm> // for sort function
using namespace std;
int main() {
// 聲明一個整數(shù)向量
vector<int> vec;
// 使用 push_back 添加元素
vec.push_back(10);
vec.push_back(20);
vec.push_back(30);
// 使用 at 訪問元素
cout << "Element at index 1: " << vec.at(1) << endl;
// 使用 operator[] 訪問元素
cout << "Element at index 2: " << vec[2] << endl;
// 使用 front 和 back 訪問首尾元素
cout << "First element: " << vec.front() << endl;
cout << "Last element: " << vec.back() << endl;
// 使用 begin 和 end 遍歷向量
cout << "Vector elements: ";
for (auto it = vec.begin(); it != vec.end(); ++it) {
cout << *it << " ";
}
cout << endl;
// 使用 size 獲取向量大小
cout << "Size of vector: " << vec.size() << endl;
// 使用 capacity 獲取向量容量
cout << "Capacity of vector: " << vec.capacity() << endl;
// 使用 resize 調(diào)整向量大小
vec.resize(5, 0); // 調(diào)整為5個元素,新元素初始化為0
cout << "After resizing, size: " << vec.size() << endl;
cout << "After resizing, elements: ";
for (int num : vec) {
cout << num << " ";
}
cout << endl;
// 使用 reserve 預(yù)先分配內(nèi)存
vec.reserve(10);
cout << "After reserving, capacity: " << vec.capacity() << endl;
// 使用 clear 清空向量
vec.clear();
cout << "After clearing, size: " << vec.size() << endl;
// 使用 empty 判斷向量是否為空
if (vec.empty()) {
cout << "Vector is empty." << endl;
}
// 使用 insert 插入元素
vec.insert(vec.begin(), 5);
vec.insert(vec.begin() + 1, 15);
cout << "After inserting elements, vector: ";
for (int num : vec) {
cout << num << " ";
}
cout << endl;
// 使用 erase 刪除元素
vec.erase(vec.begin() + 1);
cout << "After erasing element at index 1, vector: ";
for (int num : vec) {
cout << num << " ";
}
cout << endl;
// 使用 sort 對向量進(jìn)行排序
vector<int> numbers = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
sort(numbers.begin(), numbers.end());
cout << "Sorted vector: ";
for (int num : numbers) {
cout << num << " ";
}
cout << endl;
return 0;
}
列表(list): 雙向鏈表,支持插入和刪除操作
構(gòu)造函數(shù)
list<int> a{1, 2, 3};:初始化一個包含元素1, 2, 3的列表
list<int> a(n);:聲明一個包含n個元素的列表,每個元素都是0
list<int> a(n, m);:聲明一個包含n個元素的列表,每個元素都是m
list<int> a(first, last);:聲明一個列表,其元素的初始值來源于由區(qū)間所指定的序列中的元素,first和last是迭代器
插入元素
push_front(value):將元素從前面推入列表
push_back(value):在列表末尾插入一個新元素
insert(pos_iter, ele_num, ele):在指定位置插入指定數(shù)量的元素
emplace_front(args...):在列表開頭構(gòu)造元素
emplace_back(args...):在列表末尾構(gòu)造元素
刪除元素
pop_front():從正面彈出或刪除列表中的元素
pop_back():從末尾彈出或刪除列表中的元素
remove(value):刪除所有與給定值相等的元素
remove_if(predicate):刪除所有對謂詞返回true的元素
clear():刪除列表容器的所有元素,使其大小為0
檢查狀態(tài)
empty():檢查列表容器是否為空。
size():返回列表中元素的數(shù)量。
其他操作
assign(first, last):將新內(nèi)容分配給列表容器,并用新內(nèi)容替換舊內(nèi)容
sort():對列表進(jìn)行排序
reverse():反轉(zhuǎn)列表中元素的順序
merge(other_list):合并兩個已排序的列表
unique():刪除連續(xù)重復(fù)的元素
#include <iostream>
#include <list>
using namespace std;
int main() {
list<int> mylist{1, 2, 3, 4, 5};
mylist.push_front(6); // 在前面插入6
mylist.push_back(7); // 在末尾插入7
for (auto it = mylist.begin(); it != mylist.end(); ++it)
cout << ' ' << *it;
return 0;
}
映射(map): 鍵值對集合,支持快速查找,
map中的鍵必須是唯一的。如果插入一個已經(jīng)存在的鍵,新的值不會替換原有值,而是忽略插入操作,map內(nèi)部會自動根據(jù)鍵的大小進(jìn)行排序,因此遍歷時元素是按順序排列的,由于map基于紅黑樹實現(xiàn),查找、插入和刪除操作的時間復(fù)雜度為O(log n),適合需要頻繁查找和修改的場景
插入元素:
insert(pair<const Key, T> val):插入一個鍵值對
operator[]:通過鍵訪問或插入值。如果鍵不存在,則插入默認(rèn)值
訪問元素:
operator[]:通過鍵訪問值
at(const Key& key):通過鍵訪問值,如果鍵不存在則拋出異常
查找元素:
find(const Key& key):返回指向鍵為 key 的元素的迭代器,如果不存在則返回 end()
count(const Key& key):返回鍵為 key 的元素數(shù)量(對于 map,返回 0 或 1)
刪除元素:
erase(iterator position):刪除指定位置的元素
erase(const Key& key):刪除鍵為 key 的元素
clear():清空所有元素
遍歷元素:
使用迭代器遍歷,例如 for (auto it = m.begin(); it != m.end(); ++it)。
#include <iostream>
#include <map>
using namespace std;
int main() {
// 創(chuàng)建一個 map 容器
map<string, int> ageMap;
// 添加元素
ageMap["Alice"] = 30;
ageMap["Bob"] = 25;
// 訪問元素
cout << "Alice's age: " << ageMap["Alice"] << endl;
// 遍歷 map
for (const auto& pair : ageMap) {
cout << pair.first << ": " << pair.second << endl;
}
// 查找元素
if (ageMap.count("Bob")) {
cout << "Bob is in the map." << endl;
}
// 刪除元素
ageMap.erase("Alice");
// 清空 map
ageMap.clear();
return 0;
}
集合(set ): 有序集合,支持唯一元素,集合中的元素不能重復(fù),可以添加或刪除元素,但不能修改現(xiàn)有元素的值,插入元素時自動排序,元素通過鍵引用,而非容器中的位置
insert():用于向set中插入元素
erase():用于從set中刪除元素
clear():用于清除set中的所有元素
empty():用于檢查set是否為空
size():用于返回set的大小
begin():返回指向第一個元素的迭代器
end():返回指向最后一位元素的下一位的迭代器
find():查找指定元素,返回其迭代器或結(jié)束迭代器
count():統(tǒng)計集合中特定元素的數(shù)量
lower_bound():返回第一個大于等于給定值的值的地址
upper_bound():返回第一個大于給定值的值的地址
equal_range():返回一個pair,first為第一個大于等于目標(biāo)的迭代器,second為第一個大于目標(biāo)的迭代器
#include <iostream>
#include <set>
using namespace std;
int main() {
set<int> s;
s.insert(10);
s.insert(20);
s.insert(30);
// 遍歷set
for (int a : s) {
cout << a << " ";
}
cout << endl;
// 檢查元素是否存在
if (s.find(20) != s.end()) {
cout << "20 exists in set" << endl;
}
// 刪除元素
s.erase(20);
// 檢查set是否為空
if (!s.empty()) {
cout << "Set is not empty" << endl;
}
// 獲取set的大小
cout << "Size of set: " << s.size() << endl;
return 0;
}
迭代器
提供遍歷容器的統(tǒng)一接口,連接容器與算法,
begin(),end(), 迭代器是STL中泛化的指針,提供統(tǒng)一接口遍歷不同容器(如vector、list、map)的元素
1. 基礎(chǔ)遍歷(所有容器通用)
#include <vector>
#include <iostream>
using namespace std;
int main() {
vector<int> vec = {1, 2, 3, 4};
// 正向遍歷
for (auto it = vec.begin(); it != vec.end(); ++it) {
cout << *it << " "; // 輸出:1 2 3 4
}
// C++11范圍for循環(huán)(底層基于迭代器)
for (int val : vec) {
cout << val << " "; // 同上
}
return 0;
}
// 范圍 for 循環(huán)內(nèi)部實現(xiàn)基于迭代器,因此在循環(huán)中不能隨意增加或刪除元素,否則會導(dǎo)致未定義行為
2. 反向遍歷(雙向/隨機訪問迭代器支持)
list<string> words = {"hello", "world"};
for (auto rit = words.rbegin(); rit != words.rend(); ++rit) {
cout << *rit << " "; // 輸出:world hello
}
3. 插入與刪除操作
vector<int> v = {10, 20, 30};
auto it = v.begin() + 1;
it = v.insert(it, 15); // 插入后v={10,15,20,30},it指向15
it = v.erase(it); // 刪除15,it指向20(需更新迭代器?。?
4. 關(guān)聯(lián)容器特例(set/map)
set<int> s = {5, 2, 8, 1};
// 遍歷自動排序結(jié)果:1 2 5 8
for (auto it = s.begin(); it != s.end(); ++it) {
cout << *it << " ";
}
// 查找操作(返回迭代器)
auto pos = s.find(5);
if (pos != s.end()) {
s.erase(pos); // 安全刪除
}
vector的push_back()可能引發(fā)擴(kuò)容,導(dǎo)致所有迭代器失效;erase()使被刪元素及之后的迭代器失效
對vector/deque插入/刪除元素后,必須重新獲取迭代器。關(guān)聯(lián)容器(如set)刪除元素時,優(yōu)先使用it = container.erase(it) 語法
算法
獨立于容器的通用操作(排序、查找等),
sort(),find(),copy()
// sort(): 對容器進(jìn)行排序
sort(a.begin(), a.end()); // 默認(rèn)升序排序
sort(a.begin(), a.end(), greater<>()); // 使用greater對象進(jìn)行降序排序
// distance(): 計算兩個迭代器之間的距離
int dist = distance(a.begin(), a.end()); // 計算a的大小
// copy(): 將一個范圍內(nèi)的元素復(fù)制到另一個范圍
vector<int> b(a.size());
copy(a.begin(), a.end(), b.begin()); // 將a的內(nèi)容復(fù)制到b
// next_permutation(): 生成下一個排列
sort(a.begin(), a.end()); // 先排序
do {
// 處理當(dāng)前排列
} while (next_permutation(a.begin(), a.end())); // 生成所有排列
// set_intersection(): 計算兩個集合的交集
vector<int> a{1, 2, 3, 4, 5};
vector<int> b{3, 4, 5, 6, 7};
vector<int> result;
set_intersection(a.begin(), a.end(), b.begin(), b.end(), back_inserter(result)); // 計算交集
函數(shù)對象
函數(shù)對象是重載了
operator()的類對象,可像函數(shù)一樣被調(diào)用。本質(zhì)是類實例,而非函數(shù)指針,實現(xiàn)定制化操作,謂詞(如greater<int>),函數(shù)對象可包含成員變量,記錄調(diào)用間的狀態(tài)(普通函數(shù)無法做到)
#include <iostream>
#include <vector>
#include <algorithm>
struct GreaterThan {
bool operator()(int a, int b) const {
return a > b;
}
};
int main() {
std::vector<int> vec = {5, 3, 8, 1, 2};
std::sort(vec.begin(), vec.end(), GreaterThan());
for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl;
return 0;
}
適配器
修改組件接口(如容器→棧/隊列),棧
stack, 隊列queue, 優(yōu)先隊列priority_queue
棧(stack)
棧是一種后進(jìn)先出(LIFO)的數(shù)據(jù)結(jié)構(gòu)。棧適配器可以基于 deque、vector 或 list。默認(rèn)情況下,棧適配器使用 deque 作為底層容器,只能從棧頂進(jìn)行插入和刪除操作,提供 push()、pop()、top()、empty() 和 size() 等操作
#include <iostream>
#include <stack>
#include <list>
int main() {
// 創(chuàng)建一個使用 list 作為基礎(chǔ)容器的 stack 適配器
std::list<int> values{1, 2, 3};
std::stack<int, std::list<int>> my_stack(values);
// 查看 my_stack 存儲元素的個數(shù)
std::cout << "size of my_stack: " << my_stack.size() << std::endl;
// 將 my_stack 中存儲的元素依次彈棧,直到其為空
while (!my_stack.empty()) {
std::cout << my_stack.top() << std::endl; // 輸出棧頂元素
my_stack.pop(); // 彈出棧頂元素
}
return 0;
}
隊列(queue)
隊列是一種先進(jìn)先出(FIFO)的數(shù)據(jù)結(jié)構(gòu)。隊列適配器默認(rèn)使用 deque 作為底層容器,也可以使用 list,元素在隊尾插入并在前端刪除,提供 push()、pop()、front()、back()、empty() 和 size() 等操作
#include <iostream>
#include <queue>
int main() {
std::queue<int> q;
// 在隊尾壓入新元素
q.push(1);
q.push(2);
q.push(3);
// 查看隊列長度
std::cout << "size of queue: " << q.size() << std::endl;
// 將隊列中的元素依次彈出,直到其為空
while (!q.empty()) {
std::cout << q.front() << std::endl; // 輸出隊首元素
q.pop(); // 彈出隊首元素
}
return 0;
}
優(yōu)先隊列(priority_queue)
優(yōu)先隊列是一種不遵循 FIFO 或 LIFO 原則的數(shù)據(jù)結(jié)構(gòu),元素的排序基于優(yōu)先級(使用最大堆),因此具有最高優(yōu)先級的元素始終位于前端。優(yōu)先隊列適配器默認(rèn)使用 vector 作為底層容器,也可以使用 deque,元素的排序基于優(yōu)先級,提供 push()、pop()、top()、empty() 和 size() 等操作,優(yōu)先隊列適配器不能基于 list,因為 list 不支持隨機訪問操作,可以自定義比較函數(shù)來實現(xiàn)不同的優(yōu)先級規(guī)則
#include <iostream>
#include <queue>
#include <vector>
struct Node {
int x, y;
Node(int a = 0, int b = 0) : x(a), y(b) {}
};
struct cmp {
bool operator()(Node a, Node b) {
if (a.x == b.x) return a.y > b.y;
return a.x > b.x;
}
};
int main() {
std::priority_queue<Node, std::vector<Node>, cmp> q;
for (int i = 0; i < 10; ++i)
q.push(Node(rand(), rand()));
while (!q.empty()) {
std::cout << q.top().x << ' ' << q.top().y << std::endl;
q.pop();
}
return 0;
}
分配器
管理堆內(nèi)存的對象,管理內(nèi)存分配策略(默認(rèn)基于
new/delete),allocator<T>
分配器提供了多種成員函數(shù)
allocate(size_t n):分配n個對象所需的內(nèi)存
deallocate(T* p, size_t n):釋放由allocate分配的內(nèi)存
construct(T* p, const T& val):在已分配的內(nèi)存上構(gòu)造對象
destroy(T* p):銷毀對象但不解分配內(nèi)存
#include <iostream>
#include <memory>
int main() {
std::allocator<int> alloc;
int* p = alloc.allocate(5); // 分配5個int的內(nèi)存
for (int i = 0; i < 5; ++i) {
alloc.construct(p + i, i * 10); // 構(gòu)造對象
}
for (int i = 0; i < 5; ++i) {
std::cout << p[i] << " "; // 輸出: 0 10 20 30 40
}
std::cout << std::endl;
for (int i = 0; i < 5; ++i) {
alloc.destroy(p + i); // 銷毀對象
}
alloc.deallocate(p, 5); // 釋放內(nèi)存
return 0;
}
6. 文件操作
在C++中,文件
I/O(輸入/輸出)是程序與外部世界交換信息的重要手段。它允許程序從文件中讀取數(shù)據(jù)或?qū)?shù)據(jù)寫入文件。C++標(biāo)準(zhǔn)庫提供了多種文件流類來處理文件操作,文件 I/O 操作主要通過<fstream>庫實現(xiàn)
常用的文件操作包括
ifstream: 用于從文件讀取數(shù)據(jù),輸入文件流(讀操作),繼承自
istreamofstream: 用于將數(shù)據(jù)寫入文件,輸出文件流(寫操作),繼承自
ostreamfstream: 用于文件的輸入輸出,兼具讀寫功能
打開文件: 通過構(gòu)造函數(shù)或
open()成員函數(shù)打開文件,并指定文件名和打開模式讀寫操作: 使用插入(<<)或提取(>>)操作符讀取或?qū)懭胛募械臄?shù)據(jù)
關(guān)閉文件: 使用
close()函數(shù)顯式關(guān)閉文件,或者讓文件I/O變量超出作用域(文件I/O類的析構(gòu)函數(shù)會自動關(guān)閉文件
要在 C++ 中進(jìn)行文件處理,必須在 C++ 源代碼文件中包含頭文件 <iostream> 和 <fstream>
#include <fstream>
#include <iostream>
int main() {
std::ifstream src("source.png", std::ios::binary); // 二進(jìn)制讀
std::ofstream dest("copy.png", std::ios::binary); // 二進(jìn)制寫
if (!src || !dest) {
std::cerr << "文件打開失敗!" << std::endl;
return 1;
}
// 高效逐塊復(fù)制
const int BUFFER_SIZE = 4096;
char buffer[BUFFER_SIZE];
while (src.read(buffer, BUFFER_SIZE)) {
dest.write(buffer, src.gcount()); // 寫入實際讀取的字節(jié)
}
dest.write(buffer, src.gcount()); // 寫入剩余字節(jié)
src.close();
dest.close();
return 0;
}
結(jié)果:copy.png和 source.png一樣了
文件打開模式
要從文件讀取信息或者向文件寫入信息之前,首先要打開文件,在C++中,文件打開模式可以通過
std::ios_base類中的標(biāo)志來指定
ios::in: 以讀取模式打開文件
ios::out: 以寫入模式打開文件
ios::app: 以追加模式打開文件,所有新數(shù)據(jù)寫入到文件的末尾
ios::ate: 將文件指針定位到文件的末尾
ios::binary: 以二進(jìn)制模式打開文件,而不是文本模式
ios::trunc: 如果文件已存在,則刪除文件內(nèi)容
ios::nocreate: 如果文件不存在,則打開失敗
ios::noreplace: 如果文件已存在,則打開失敗
#include <iostream>
#include <fstream>
using namespace std;
void main()
{
ofstream outFile;
outFile.open("number.txt", ios::app);
if (!outFile.is_open())
{
cout << " problem with opening the file ";
}
else
{
outFile << 200 << endl;
cout << "done writing" << endl;
}
outFile.close();
}
輸出:done writing
讀取文件
讀取文件時,可以使用
std::ifstream類,使用 >> 或getline讀取文件內(nèi)容1. 包含頭文件:
#include <fstream>// 核心文件流庫
#include <string>// 用于getline等操作2. 創(chuàng)建流對象:
std::ifstream fin;//ifstream專用于輸入(讀取)3. 打開文件并校驗:
fin.open("data.txt", std::ios::in); // 文本模式打開
if (!fin.is_open()) { // 必須檢查是否成功打開
std::cerr << "文件打開失?。?;
return -1;
}
模式選項(可組合使用):
std::ios::in(讀)、std::ios::binary(二進(jìn)制)、std::ios::ate(初始定位到文件末尾)4. 讀取數(shù)據(jù): 四種核心讀取方法
方法 1: 運算符 >>(簡單但有限制),適用場景: 讀取無空格的數(shù)據(jù)(如數(shù)字、單詞),缺陷: 忽略空格和換行,不保留原始格式
char buffer[100];
while (fin >> buffer) { // 遇空格/換行終止
std::cout << buffer << std::endl;
}
方法 2:
std::getline(推薦按行處理),優(yōu)勢: 保留行內(nèi)空格,內(nèi)存安全(自動處理字符串內(nèi)存)
std::string line;
while (std::getline(fin, line)) { // 讀取整行(含空格)
std::cout << line << std::endl;
}
方法 3: 流對象的
getline方法,注意: 需預(yù)設(shè)緩沖區(qū)大小,避免溢出
char buffer[100];
while (fin.getline(buffer, 100)) { // 讀取到字符數(shù)組
std::cout << buffer << std::endl;
}
方法 4: 逐字符讀?。?
get()),用途: 需精細(xì)控制字符時(如解析特殊結(jié)構(gòu))缺點: 效率低,大文件不推薦
char ch;
while (fin.get(ch)) { // 逐個字符讀取
std::cout << ch;
}
特殊場景處理:二進(jìn)制文件讀取 必須指定
std::ios::binary模式
std::ifstream binFile("data.bin", std::ios::binary);
char* data = new char[1024];
binFile.read(data, 1024); // 直接讀取字節(jié)塊
binFile.close();
5. 關(guān)閉文件:
fin.close();// 顯式關(guān)閉釋放資源
二進(jìn)制模式比文本模式更快(無換行符轉(zhuǎn)換)
寫入文件
std::ofstream(輸出文件流): 專用于寫入文件,需包含頭文件
<fstream>
#include <fstream>
int main() {
std::ofstream fout("data.txt"); // 創(chuàng)建或覆蓋文件
if (fout.is_open()) {
fout << "Hello, World!\n"; // 寫入文本
fout.close(); // 必須關(guān)閉
}
return 0;
}
std::fstream(多功能文件流): 支持讀寫操作,通過模式參數(shù)控制
std::fstream file("data.txt", std::ios::out | std::ios::app); // 追加模式
file << "New line\n";
file.close();
寫入數(shù)據(jù)方法
1. 文本寫入: 使用 << 操作符(類似
cout)
fout << "Integer: " << 42 << "\nFloat: " << 3.14;
2. 二進(jìn)制寫入: 用 write() 直接寫入內(nèi)存數(shù)據(jù)
int data = 100;
fout.write(reinterpret_cast<char*>(&data), sizeof(data)); // 二進(jìn)制寫入
3. 格式化控制: 設(shè)置精度與寬度
fout.precision(2); // 保留2位小數(shù)
fout.width(10); // 輸出寬度10字符
fout << 3.14159; // 輸出 " 3.14"
性能優(yōu)化與安全
1. 緩沖區(qū)管理: 默認(rèn)有緩沖區(qū),手動刷新可避免數(shù)據(jù)丟失
fout << "Important data";
fout.flush(); // 立即寫入磁盤
調(diào)整緩沖區(qū)大小(減少I/O次數(shù)):
char buffer[8192]; // 8KB緩沖區(qū)
fout.rdbuf()->pubsetbuf(buffer, sizeof(buffer));
2. 錯誤處理: 檢查文件是否成功打開
if (!fout) {
std::cerr << "File open failed!";
return -1;
}
3. 線程安全: 多線程寫入時需加鎖(如
std::mutex)
高級場景
1. 追加數(shù)據(jù)
std::ofstream log("log.txt", std::ios::app);
log << "Event: User login\n";
2. 大文件處理: 分塊寫入避免內(nèi)存溢出
std::vector<char> largeData(1000000, 'A'); // 1MB數(shù)據(jù)
fout.write(largeData.data(), largeData.size());
3. 跨平臺路徑處理: 使用
std::filesystem(C++17起)
#include <filesystem>
std::ofstream fout(std::filesystem::path("data/demo.txt"));
#include <fstream>
#include <iostream>
int main() {
// 1. 打開文件(追加模式)
std::ofstream fout("output.txt", std::ios::app);
if (!fout) {
std::cerr << "Error opening file!";
return 1;
}
// 2. 寫入數(shù)據(jù)
fout << "---- New Entry ----\n";
fout << "Timestamp: " << time(nullptr) << "\n";
// 3. 檢查寫入狀態(tài)
if (fout.fail())
std::cerr << "Write failed!";
// 4. 關(guān)閉文件
fout.close();
return 0;
}
文件常見問題及解決方案
1. 檢查文件是否成功打開: 使用
is_open()成員函數(shù)檢查文件是否成功打開,如果未成功,則采取相應(yīng)措施2. 顯式關(guān)閉文件: 盡管C++流對象在析構(gòu)時會自動關(guān)閉文件,顯式調(diào)用
close()函數(shù)可以提高代碼的清晰度和控制力3. 使用追加或讀寫模式: 如果需要在文件末尾追加內(nèi)容,可以使用
ios::app模式;如果需要讀寫同一文件,可以使用ios::in | ios::out模式4. 注意字符編碼: 明確文件的編碼格式,必要時使用第三方庫處理不同編碼的讀寫