C++ 基礎(chǔ)入門

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 LibrarySTL )提供了大量的容器、算法和迭代器,方便開發(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 、shortlong 、long long 以及它們的無符號版本 unsigned intunsigned short 、unsigned long 、unsigned long long 。這些類型用于存儲整數(shù)數(shù)據(jù),其范圍和精度取決于具體類型。例如,int 通常占用 4 個字節(jié),而 short 通常占用 2 個字節(jié)

浮點類型: 用于表示實數(shù),包括 float 、doublelong doublefloat 通常占用 4 個字節(jié),可以存儲 7 位小數(shù);double 通常占用 8 個字節(jié),可以存儲 15 位小數(shù);long double 通常占用 16 個字節(jié),精度更高

字符類型: 用于表示單個字符,包括 charwchar_t 。char 通常占用 1 個字節(jié),而 wchar_t 用于表示寬字符,但其大小由實現(xiàn)定義,不可靠

布爾類型: 用于表示布爾值,即 truefalse,通常占用 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)存位置,因此只能同時存儲一個元素

  1. 聯(lián)合的大小等于其成員中最大的成員的大小

  2. 聯(lián)合不能作為基類使用,因為它不能繼承自其他類

  3. 聯(lián)合不能包含引用類型的成員,因為引用需要額外的存儲空間

  4. 匿名聯(lián)合不使用點運算符,其成員必須是數(shù)據(jù)類型,不允許有成員函數(shù)或私有/受保護(hù)的成員,為了防止訪問錯誤

  5. 建議在聯(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 = &num; // 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 constructoroperator 被刪除),但允許移動(move constructormove 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_sharednew 更高效,且避免內(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 、switchfor 、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ù),輸入文件流(讀操作),繼承自 istream

ofstream: 用于將數(shù)據(jù)寫入文件,輸出文件流(寫操作),繼承自 ostream

fstream: 用于文件的輸入輸出,兼具讀寫功能

打開文件: 通過構(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. 注意字符編碼: 明確文件的編碼格式,必要時使用第三方庫處理不同編碼的讀寫

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容