掌握C++核心特性

目標: 掌握C++核心特性,為嵌入式開發打基礎

好的,我來為你詳細梳理一下 繼承與多態、虛函數 相關的知識點,包括單繼承、多繼承、虛函數表機制、純虛函數與抽象類、動態綁定。以下內容適合中等難度層次的理解,便于考試復習或面試準備。


🌟 繼承與多態,虛函數


1?? 單繼承和多繼承

單繼承
  • 一個派生類只有一個基類。

  • 結構簡單,層次清晰。

  • 示例:

    class Base {
    public:void show() { std::cout << "Base" << std::endl; }
    };class Derived : public Base {
    public:void display() { std::cout << "Derived" << std::endl; }
    };
    

public 公開繼承(最常用,基類 public 和 protected 成員在派生類中保持原有權限)

protected 保護繼承(基類 public 和 protected 成員在派生類中都變成 protected)

private 私有繼承(基類 public 和 protected 成員在派生類中都變成 private)

多繼承
  • 一個派生類可以同時繼承多個基類。

  • 可帶來靈活性,但也可能引發二義性問題(如菱形繼承問題)。

  • 解決辦法:虛繼承(virtual 關鍵字)

  • 示例:

    class Base1 {
    public:void func1() { std::cout << "Base1" << std::endl; }
    };class Base2 {
    public:void func2() { std::cout << "Base2" << std::endl; }
    };class Derived : public Base1, public Base2 {
    };
    
菱形繼承問題
class A { public: int x; };
class B : virtual public A { };
class C : virtual public A { };
class D : public B, public C { };

👉 如果 BC 都虛繼承自 AD 中只有一份 A 的成員。


🚀 構造函數后 : 的用途 —— 初始化列表

在 C++ 中,構造函數定義時可以用 : 引出一個 初始化列表,用于初始化成員變量和基類。

語法

class 類名 {
public:類型1 成員1;類型2 成員2;類名(參數列表) : 成員1(1), 成員2(2) {// 構造函數體}
};

🌟 初始化列表主要作用

? 初始化 const 成員(必須用初始化列表)
? 初始化 引用成員(必須用初始化列表)
? 調用基類構造函數(在繼承中必用)
? 效率更高(成員在進入構造函數體之前就已初始化)


🌰 示例 1:普通成員初始化

class Point {int x;int y;
public:Point(int a, int b) : x(a), y(b) {// 構造函數體可以為空}
};

等價于:

Point p(1, 2);

這里 x 初始化為 1,y 初始化為 2。


🌰 示例 2:const 和引用成員

class Example {const int ci;int& ref;
public:Example(int i, int& r) : ci(i), ref(r) { }
};

👉 注意:const& 成員必須在初始化列表里賦值,不能在構造函數體內賦值。


🌰 示例 3:繼承情況下,調用基類構造函數

class Base {
public:Base(int a) { std::cout << "Base: " << a << std::endl; }
};class Derived : public Base {
public:Derived(int a, int b) : Base(a) {std::cout << "Derived: " << b << std::endl;}
};

當你寫:

Derived d(10, 20);

輸出:

Base: 10
Derived: 20

? 先調用基類構造函數。


用法符號 : 位置例子
類繼承聲明class A : public Bclass B : public A { };
初始化列表構造函數頭部后A(int x) : a(x) { }

🌟 初始化列表主要作用

? 初始化 const 成員(必須用初始化列表)
? 初始化 引用成員(必須用初始化列表)
? 調用基類構造函數(在繼承中必用)
? 效率更高(成員在進入構造函數體之前就已初始化)

普通 const 成員是屬于對象的,每個對象的 const 成員值可能不同。
必須在構造對象時確定 const 成員值,所以需要初始化列表。
👉 引用必須在定義時綁定到對象(或變量)上,不能晚綁定。
一旦引用被初始化(綁定),它就永遠指向這個對象或變量,不可改變。


特性const 成員變量static const 成員變量
初始化方式必須用初始化列表初始化可以在類內初始化
屬于對象的每個實例類的所有實例共享一份
例子A(int v) : x(v) {}static const int x = 10;

2?? 虛函數表機制

  • 虛函數表 (vtable):編譯器為含有虛函數的類生成的一張函數地址表。
  • 虛指針 (vptr):每個含虛函數的對象實例中包含一個指向虛函數表的指針。
  • 派生類覆蓋虛函數時,vtable 的相應入口會被派生類的函數地址替換。
  • 調用虛函數時,根據 vptr 定位 vtable,再調用正確的函數地址,實現多態。
class Base {
public:virtual void func() { std::cout << "Base::func" << std::endl; }
};class Derived : public Base {
public:void func() override { std::cout << "Derived::func" << std::endl; }
};

📌 當你寫 Base* p = new Derived(); p->func(); 時,會通過 vptr 查找 vtable 中的 Derived::func 地址。


3?? 純虛函數和抽象類

純虛函數
  • 語法:virtual void func() = 0;
  • 沒有實現,需要派生類重寫。
抽象類
  • 包含至少一個純虛函數的類。
  • 無法實例化對象,只能作為基類。

示例:

class Shape {
public:virtual void draw() = 0; // 純虛函數
};class Circle : public Shape {
public:void draw() override { std::cout << "Draw Circle" << std::endl; }
};

👉 Shape s; ?不允許
👉 Shape* ps = new Circle(); ?允許,用基類指針指向子類對象。


4?? 動態綁定

  • 動態綁定(又稱 運行時多態):在運行時確定調用哪個函數。

  • 前提:

    • 函數是虛函數。
    • 通過基類指針或引用調用。
  • 如果不滿足上面條件,編譯時靜態綁定。

示例:

Base* p = new Derived();
p->func(); // 動態綁定,調用 Derived::func

如果是 p->Base::func(); 則會靜態綁定,強制調用基類版本。


💡 總結

特性單繼承多繼承虛函數純虛函數動態綁定
關系一個基類多個基類實現多態實現接口運行時確定函數
優點簡單易維護靈活多態行為強制派生類實現多態支持
缺點功能受限易引發二義性增加內存開銷不能實例化基類性能略低于靜態綁定

繼承與 vptr 重新賦值的背景

  • 每個對象的 vptr 用來指向當前對象所屬類的虛函數表(vtable)。
  • 當你創建一個派生類對象時,這個對象其實包含了基類子對象部分。
  • 在構造過程中,隨著構造函數的調用,vptr 會被設置為對應類的 vtable

兩個對象:
各自有一個 vptr
vptr 都指向同一張 vtable(Derived 的 vtable)

vtable 是編譯器為類生成的唯一一張表(每個帶虛函數的類一張)


🌟 C++ 模板基礎


1?? 函數模板語法

👉 語法:

template <typename T>
T add(T a, T b) {return a + b;
}

或:

template <class T>  // typename 和 class 都可以
T add(T a, T b) {return a + b;
}

👉 使用:

add(3, 4);      // T 推導為 int
add(3.5, 4.2);  // T 推導為 double
add<int>(3, 4); // 顯式指定 T = int

2?? 類模板實現

👉 語法:

template <typename T>
class MyClass {
public:T data;MyClass(T val) : data(val) {}void show() { std::cout << data << "\n"; }
};

👉 使用:

MyClass<int> obj1(10);
MyClass<std::string> obj2("hello");

👉 注意:

  • 必須在使用類模板時提供模板參數(除非有默認參數)。
  • 類模板成員函數定義可以在類外,但必須帶模板頭:
template <typename T>
void MyClass<T>::show() {std::cout << data << "\n";
}

3?? 模板特化

👉 全特化:

template <typename T>
class Printer {
public:void print(T val) { std::cout << val << "\n"; }
};// 對 char* 的特化
template <>
class Printer<char*> {
public:void print(char* val) { std::cout << "char* : " << val << "\n"; }
};

👉 偏特化:

template <typename T, typename U>
class Pair { /* ... */ };template <typename T>
class Pair<T, int> { /* 針對第二個參數是 int 的特化 */ };

4?? 模板參數推導

👉 函數模板支持參數推導:

template <typename T>
void func(T val) { /* ... */ }func(10);         // 推導 T=int
func(3.14);       // 推導 T=double
func("hello");    // 推導 T=const char*

👉 類模板 不支持自動推導(C++17 前),但 C++17 起支持 類模板參數推導引擎

template <typename T>
class Wrapper {
public:T value;Wrapper(T v) : value(v) {}
};Wrapper w(10);  // C++17 起推導出 Wrapper<int>

🌟 小結表

模板特性作用特點
函數模板讓函數支持不同類型支持參數推導,可顯式指定
類模板讓類支持不同類型使用時需指定參數(除非 C++17 推導)
模板特化針對特定類型提供不同實現全特化或偏特化
參數推導自動根據實參確定模板參數類模板一般不推導,函數模板支持

🌟 C++ 異常處理基礎


1?? 異常處理機制

基本語法:
try {// 可能拋出異常的代碼throw std::runtime_error("Error occurred");
}
catch (const std::runtime_error& e) {std::cout << "Caught: " << e.what() << "\n";
}
catch (...) {std::cout << "Caught unknown exception\n";
}

👉 流程

  • try 塊中代碼運行時遇到 throw,立即停止執行,跳轉到對應 catch
  • 匹配的 catch 語句被調用。
  • 如果沒有匹配的 catch,程序調用 std::terminate()

👉 注意

  • throw; 可以重新拋出當前捕獲的異常。
  • 異常匹配是按照 catch 的順序自上而下。

2?? 自定義異常類

你可以自定義異常類型,通常繼承自 std::exception 或其子類。

class MyException : public std::exception {
public:const char* what() const noexcept override {return "My custom exception";}
};

使用:

try {throw MyException();
}
catch (const MyException& e) {std::cout << e.what() << "\n";
}

3?? RAII 與異常安全

RAII(資源獲取即初始化)是 C++ 保證異常安全的重要手段。

例子:
class FileWrapper {FILE* fp;
public:FileWrapper(const char* filename) {fp = fopen(filename, "r");if (!fp) throw std::runtime_error("File open failed");}~FileWrapper() {if (fp) fclose(fp);}
};

? 如果在構造中拋出異常,已構造對象的析構函數會被自動調用,資源得到釋放。
? 這就是 異常安全 的 RAII 精神。


4?? 嵌入式系統中異常使用注意事項

👉 為什么嵌入式常禁用異常?

  • 異常會增加代碼體積(嵌入式對ROM/Flash大小敏感)
  • 異常處理可能需要棧展開,增加運行時開銷
  • 嵌入式通常要求可控、確定性的錯誤處理

👉 替代方案

  • 返回錯誤碼
  • 使用斷言 assert
  • 用狀態機或專用錯誤處理函數

👉 嵌入式項目編譯器一般禁用異常

g++ -fno-exceptions ...

🌟 小結表

特性描述注意事項
try-catch-throw異常處理結構,用于捕獲和處理異常匹配順序重要,throw 可重新拋出
自定義異常類提供更清晰的異常類型繼承自 std::exception,重寫 what()
RAII自動管理資源,防止泄漏析構函數釋放資源,保證異常安全
嵌入式異常通常禁用,因開銷大推薦用錯誤碼或斷言代替

🌟 C++ 異常為什么會增大代碼空間?

因為 編譯器為了支持異常處理,需要生成額外的元數據、表和隱藏代碼。主要包括以下幾個方面:


1?? 棧展開(stack unwinding)信息

👉 當你 throw 異常時,程序必須從拋出點開始,依次調用每個對象的析構函數,正確釋放資源。

💡 為了做到這點,編譯器會:

  • 在二進制中生成一個“異常處理表”(也叫 棧展開表unwind table
  • 記錄每個函數的棧幀布局、哪些地方有局部對象、析構函數地址等

? 這些表存在于可執行文件中(通常是 .eh_frame 段),直接占用代碼空間。


2?? 異常處理控制邏輯

👉 編譯器生成隱藏代碼:

  • 檢測拋出異常的地方
  • 跳轉到異常處理器(catch 代碼)
  • 調用析構函數做清理

這些代碼雖然不顯式寫在源代碼中,但會體現在最終的機器碼中。


3?? 多余的輔助代碼 / 運行庫支持

👉 異常處理需要運行庫提供輔助函數,比如:

  • 異常對象的創建、復制、銷毀邏輯
  • 拋出異常時調用的全局函數(例如 __cxa_throw__cxa_begin_catch 等,GCC/Clang 下)

👉 這部分庫代碼也會被鏈接進你的程序中,增加體積。


🌈 對比:C vs C++ 錯誤處理

特性C語言C++異常
機制返回值、errnosetjmp/longjmptry-catch-throw
自動清理? 無,必須手工清理? 自動調用析構,RAII
錯誤傳播必須層層傳遞或顯式 longjmp自動沿調用棧尋找 catch
可讀性易出錯、代碼繁瑣更簡潔、可讀性好
性能開銷小,簡單高效異常表增加代碼體積,棧展開有開銷

🌟 你問得很好!咱們一起把“C語言需要根據錯誤手動操作、層層處理”這個概念徹底搞清楚。


🔑 為什么說 C 需要“層層”處理錯誤?

在 C 語言里,函數出錯后,它本身不會“跳回去”或者自動通知調用者發生了什么(不像 C++ 異常可以自動向上傳遞)。
👉 你得 顯式返回錯誤碼,并在每一層調用代碼里手工檢查。


🌰 例子:層層返回錯誤碼

假設你寫一個程序,調用很多函數,某一層出錯了,需要上層知道:

#include <stdio.h>int lowLevel(int a) {if (a == 0) {return -1;  // 出錯:除 0}return 100 / a;
}int midLevel(int a) {int res = lowLevel(a);if (res == -1) {return -1;  // 出錯,繼續向上報告}return res + 10;
}int highLevel(int a) {int res = midLevel(a);if (res == -1) {return -1;  // 出錯,繼續向上報告}return res * 2;
}int main() {int result = highLevel(0);if (result == -1) {printf("Error happened!\n");} else {printf("Result = %d\n", result);}
}

? 這就是所謂 層層檢查、層層返回
每一層都必須寫:

if (返回值 == 錯誤碼) return 錯誤碼;

否則,錯誤就會“漏掉”。


🔥 和 C++ 異常對比:自動向上傳遞

同樣邏輯如果是 C++:

int lowLevel(int a) {if (a == 0) throw std::runtime_error("div by zero");return 100 / a;
}int midLevel(int a) {return lowLevel(a) + 10;
}int highLevel(int a) {return midLevel(a) * 2;
}int main() {try {int result = highLevel(0);std::cout << "Result = " << result << "\n";} catch (const std::exception& e) {std::cout << "Error: " << e.what() << "\n";}
}

? 你不用每層寫“檢查返回值、return -1”了,異常會自動穿過每一層,直到 catch


🌈 層層檢查的含義總結
C 語言層層檢查C++ 異常
每層手動檢查返回值或 errno異常自動向上傳遞到最近的 catch
錯誤處理代碼和主邏輯混在一起,容易混亂錯誤處理代碼集中在 catch,主邏輯更清晰
易寫漏檢查,容易埋 bug不易漏掉錯誤(除非沒有 catch

? C 的手工層層處理的風險

  • 容易漏掉某層的檢查,錯誤被默默吞掉
  • 錯誤碼設計混亂時,調試困難
  • 錯誤處理代碼重復多,維護麻煩

🚀 STL 容器基礎 (vector, array, list)


1?? 容器的選擇原則

? vector

  • 連續內存塊(像動態數組)
  • 隨機訪問快(支持 []at()
  • 尾部插入/刪除快(push_back / pop_back O(1))
  • 中間/開頭插入刪除慢(需移動元素)

? array

  • 固定大小、棧分配(本質是封裝了 C 風格數組)
  • 編譯期大小確定
  • 非常輕量、效率高(零開銷封裝)

? list

  • 雙向鏈表
  • 任意位置插入/刪除 O(1)
  • 不支持隨機訪問(無 []

💡 選擇思路:

需求容器
需要頻繁隨機訪問vectorarray
需要頻繁插入刪除(中間或兩端)list
大小固定、性能極高array
動態大小、插尾快vector

2?? 迭代器使用

所有 STL 容器都支持迭代器,用于統一遍歷:

#include <vector>
#include <iostream>int main() {std::vector<int> v = {1, 2, 3, 4};// 普通迭代器for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {std::cout << *it << " ";}std::cout << "\n";// 范圍 for(C++11)for (auto x : v) {std::cout << x << " ";}std::cout << "\n";// 反向迭代器for (auto it = v.rbegin(); it != v.rend(); ++it) {std::cout << *it << " ";}std::cout << "\n";
}

👉 注意:

  • vector:隨機訪問迭代器
  • list:雙向迭代器(不能做隨機訪問操作)

3?? 算法庫基礎

STL 提供強大算法,與容器無關:

#include <algorithm>
#include <vector>
#include <iostream>int main() {std::vector<int> v = {4, 1, 3, 2};std::sort(v.begin(), v.end());  // 排序std::for_each(v.begin(), v.end(), [](int x) {std::cout << x << " ";});std::cout << "\n";auto it = std::find(v.begin(), v.end(), 3);  // 查找if (it != v.end()) {std::cout << "Found: " << *it << "\n";}
}

💡 STL 算法特點:

  • 和容器解耦(基于迭代器工作)
  • 提供排序、查找、修改、統計等功能
  • 支持自定義謂詞(lambda、函數對象)

4?? 嵌入式環境下 STL 使用注意事項

嵌入式開發中 STL 使用會遇到一些問題:

? 動態分配

  • vectorlist 都依賴堆內存,嵌入式堆內存可能緊張或管理嚴格。

? 代碼膨脹

  • 模板、泛型帶來代碼體積增大。

? 實時性

  • 有些操作(如 vector 擴容)可能會帶來不可預測的耗時。

? 對策

  • 優先選用 array(棧上固定大小,零開銷封裝)
  • 或自定義 allocator 控制內存管理
  • 或使用輕量級替代庫(如:Embedded STL、ETL)

🌈 小結

容器優勢適用場景
vector動態大小、隨機訪問快數組替代、需要動態增長
array固定大小、棧分配、零開銷大小固定、嵌入式友好
list插入刪除快、穩定迭代器頻繁中間操作、元素數量不大
嵌入式建議
盡量避免堆分配容器(vector、list)
array 或靜態分配的容器
控制代碼體積(注意模板實例化膨脹)

迭代器類自己重載了運算符

這些迭代器類型都會重載必要的運算符,比如:

  • *it —— 重載了 operator*(),返回元素引用
  • ++it —— 重載了 operator++(),讓迭代器移動到下一個元素
  • --it —— 重載了 operator--()(如果支持)
  • it + nit - n —— 只有隨機訪問迭代器(如 vector)會重載
  • it == it2it != it2 —— 重載了比較運算符

? 不同容器的迭代器能力不同

容器迭代器類型支持運算符
vector / array隨機訪問迭代器++, --, +, -, []
list雙向迭代器++, --
forward_list單向迭代器只能 ++

🚀 智能指針 (unique_ptr, shared_ptr)

智能指針(std::unique_ptr, std::shared_ptr, std::weak_ptr)
→ 屬于 C++ 標準庫的一部分
→ 定義在 頭文件中
→ 實現基于模板、RAII、引用計數等技術,但不歸類在 STL 容器/算法里

1?? RAII 原理

RAII 全稱:
👉 Resource Acquisition Is Initialization
👉 資源獲取即初始化

🚀 RAII 的核心思想

把資源(內存、文件句柄、鎖、網絡連接等)的管理交給對象的生命周期。

也就是說:

  • 對象創建時(構造函數):獲取資源
  • 對象銷毀時(析構函數):釋放資源

💡 用對象的生命周期保證資源安全,不需要手工釋放。

🌈 RAII 的應用場景

  • 智能指針(unique_ptr, shared_ptr)管理內存
  • fstream 管理文件句柄
  • lock_guard / unique_lock 管理鎖
  • 各種容器管理內部數據
  • 自定義資源類(比如管理數據庫連接、網絡 socket)

🌰 RAII 例子

裸指針(非 RAII)

void foo() {int* p = new int(10);// ... 使用 pdelete p;  // 別忘了!否則內存泄漏
}

? 如果程序中途拋異常或 return,可能忘了 delete。

💡 智能指針就是 RAII 的經典應用:

  • 構造時接管裸指針(new 的結果)
  • 析構時自動 delete,防止內存泄漏
{std::unique_ptr<int> p(new int(10)); // 自動管理內存,無需手動 delete
} // 作用域結束,自動 delete


? RAII 的實現邏輯

RAII 的實現 = 用類封裝資源

  • 構造函數:負責獲取資源
  • 析構函數:負責釋放資源

資源對象的生命周期 = 資源的生命周期
對象被創建時 → 自動獲得資源
對象被銷毀時 → 自動釋放資源


🌰 手寫一個簡單 RAII 類(以文件操作為例)

我們不用 std::ofstream,自己實現一個簡單 RAII 文件類:

#include <iostream>
#include <cstdio>class FileRAII {
private:FILE* file;public:// 構造函數:打開文件FileRAII(const char* filename, const char* mode) {file = std::fopen(filename, mode);if (!file) {throw std::runtime_error("Failed to open file");}std::cout << "File opened\n";}// 提供操作文件的方法void write(const char* text) {if (file) {std::fputs(text, file);}}// 析構函數:關閉文件~FileRAII() {if (file) {std::fclose(file);std::cout << "File closed\n";}}// 禁止拷貝,防止重復關閉FileRAII(const FileRAII&) = delete;FileRAII& operator=(const FileRAII&) = delete;
};

🌟 使用例子

int main() {try {FileRAII file("test.txt", "w");file.write("Hello RAII!");// 不需要手工 fclose,離開作用域自動關閉} catch (const std::exception& e) {std::cerr << e.what() << "\n";}return 0;
}

🌈 RAII 實現的關鍵點
部分作用
構造函數獲取資源(例如分配內存、打開文件、加鎖)
析構函數釋放資源(例如釋放內存、關閉文件、解鎖)
禁止拷貝防止多個對象共享同一資源導致重復釋放
可選:支持移動語義允許資源轉移所有權(C++11 以后推薦)

RAII 本質就是編寫一個資源封裝類,通過構造 + 析構管理資源,讓對象生命周期決定資源管理,無需手工操作。


🚀 RAII + 異常處理:優雅管理資源

在 C++ 中,經常這樣用:

try {SomeRAIIObject obj;// ... 可能拋異常的代碼 ...
} catch (...) {// 處理異常
}
// 無論如何 obj 析構、資源釋放

💡 這就是 現代 C++ 推薦風格:RAII 負責資源安全,異常處理負責邏輯控制。

2?? unique_ptr 使用場景

? 特點

  • 獨占所有權(禁止拷貝,只允許移動)
  • 不允許多個 unique_ptr 管同一塊內存

? 使用場景

  • 確保資源唯一所有權
  • 避免手寫 delete,防止泄漏
  • 用在工廠函數、返回局部對象時:
std::unique_ptr<Foo> createFoo() {return std::unique_ptr<Foo>(new Foo);
}
  • 用于指向大對象、避免復制

? 移動所有權

std::unique_ptr<int> p1(new int(10));
std::unique_ptr<int> p2 = std::move(p1);  // p1 放棄所有權

3?? shared_ptr 引用計數

? 特點

  • 多個 shared_ptr 可以共享一塊內存的所有權
  • 內部維護一個 引用計數
  • 最后一個 shared_ptr 被銷毀時,資源才被釋放

? 引用計數機制

auto p1 = std::make_shared<int>(10);
auto p2 = p1;  // 引用計數 +1
auto p3 = p2;  // 引用計數 +1
// p1, p2, p3 全部銷毀后,delete 內存

你可以通過 use_count() 查看計數:

std::cout << p1.use_count();  // 輸出當前計數

4?? 避免循環引用

? 循環引用問題
如果兩個對象都用 shared_ptr 指向對方,會導致引用計數永遠不為 0,內存無法釋放。

💡 示例

struct B;
struct A {std::shared_ptr<B> bptr;
};
struct B {std::shared_ptr<A> aptr;
};

💥 這里 A 和 B 相互持有 shared_ptr,會產生循環引用。

? 解決方案

weak_ptr 打破循環

struct B;
struct A {std::shared_ptr<B> bptr;
};
struct B {std::weak_ptr<A> aptr;  // 不增加引用計數
};
  • weak_ptr 不會增加引用計數,只是觀測對象是否還活著。
  • lock() 可以臨時獲得一個 shared_ptr
if (auto sp = aptr.lock()) {// 安全訪問
}

🌈 小結對比

智能指針特點適用場景
unique_ptr獨占、禁止拷貝獨占資源,防止泄漏
shared_ptr引用計數、共享資源多方共享、動態生命周期管理
weak_ptr弱引用、不增加計數避免循環引用、觀察 shared_ptr

🌟 C++ 智能指針主要就是 unique_ptr, shared_ptr, weak_ptr 三種,每種針對不同的所有權管理需求,配合 RAII 自動管理內存,防止泄漏。



? RAII 類本身不需要配合智能指針

👉 RAII 類 = 自己封裝了資源管理(構造獲取資源 + 析構釋放資源)
👉 它的資源管理已經是安全的,不依賴智能指針。

🌰 比如:

{FileRAII file("test.txt", "w");file.write("Hello RAII");// 離開作用域時自動關閉文件,不需要智能指針
}

? 這里 RAII 類的對象本身就在棧上,出作用域自動銷毀,資源釋放。根本不需要智能指針參與。


🌈 什么時候會配合智能指針使用?

你可能會 動態創建 RAII 對象,這時:

  • 如果你用 new 創建 RAII 對象,為避免手工 delete,就用智能指針管理它。
  • 特別是當 RAII 對象需要跨作用域、多處共享時,智能指針(如 unique_ptr / shared_ptr)就很方便。

🌰 示例:

#include <memory>
auto filePtr = std::make_unique<FileRAII>("test.txt", "w");
filePtr->write("Hello RAII");
// unique_ptr 離開作用域自動釋放 FileRAII 對象,FileRAII 析構釋放資源

👉 這里智能指針管理 RAII 對象本身的生命周期,RAII 對象內部管理資源的生命周期。


? 智能指針和 RAII 類的關系總結

情況是否需要智能指針
RAII 類對象在棧上聲明(局部變量)? 不需要智能指針,作用域退出時自動釋放
RAII 類對象動態創建(new)? 推薦用智能指針管理(避免手工 delete)
RAII 類對象需要跨多個作用域共享? 用 shared_ptr
RAII 類對象轉移所有權? 用 unique_ptr

📝 核心思路

RAII 解決資源管理,智能指針解決對象管理。它們可以單獨用,也可以組合用,取決于對象的使用方式。


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/87979.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/87979.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/87979.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python的高校教師資源管理系統

目錄 技術棧介紹具體實現截圖系統設計研究方法&#xff1a;設計步驟設計流程核心代碼部分展示研究方法詳細視頻演示試驗方案論文大綱源碼獲取/詳細視頻演示 技術棧介紹 Django-SpringBoot-php-Node.js-flask 本課題的研究方法和研究步驟基本合理&#xff0c;難度適中&#xf…

Java Collections工具類:高效集合操作

Collections工具類概述 Collections是Java提供的集合操作工具類&#xff0c;位于java.util包中&#xff0c;包含大量靜態方法&#xff0c;用于對List、Set、Map等集合進行排序、查找、替換、同步化等操作。 常用方法及代碼示例 排序操作 sort(List<T> list)&#xff1a…

vue指令總結

vue指令總結 一、總述 二、代碼實現&#xff08;內含大量注釋&#xff09; <!DOCTYPE html> <html> <head><meta charset"utf-8"><title>vue入門</title><!-- 使用Vue 3官方CDN --><script src"https://unpkg.c…

RUP——統一軟件開發過程

RUP概述 RUP&#xff08;Rational Unified Process&#xff09;&#xff0c;統一軟件開發過程&#xff0c;統一軟件過程是一個面向對象且基于網絡的程序開發方法論。 在RUP中采用“41”視圖模型來描述軟件系統的體系結構。“41”視圖包括邏輯視圖、實現視圖、進程視圖、部署視…

SpringBoot電腦商城項目--增加減少購物車商品數量

1. 持久層 1.1 規劃sql語句 執行更新t_cart表記錄的num值根據cid查詢購物車的數據是否存在 select * from t_cart where cid#{cid} 1.2 接口和抽象方法 /*** 獲取購物車中商品的數據總數* return 購物車中商品的數據總數*/Cart findByCid(Integer cid); 1.3 xml文件中sql映射…

零基礎學習Redis(13) -- Java使用Redis命令

上期我們學習了如何使用Java連接到redis&#xff0c;這期我們來學習如何在java中使用redis中的一些命令 1. set/get 可以看到jedis類中提供了很多set方法 public static void test1(Jedis jedis) {jedis.flushAll();jedis.set("key1", "v1");jedis.set(&q…

解決OSS存儲桶未創建導致的XML錯誤

前言 在Java開發中&#xff0c;集成對象存儲服務&#xff08;OSS&#xff09;時&#xff0c;開發者常會遇到一個令人困惑的錯誤提示&#xff1a; “This XML file does not appear to have any style information associated with it. The document tree is shown below.” 此…

Spring 表達式語言(SpEL)深度解析:從基礎到高級實戰指南

目錄 一、SpEL是什么&#xff1f;為什么需要它&#xff1f; 核心價值&#xff1a; 典型應用場景&#xff1a; 二、基礎語法快速入門 1. 表達式解析基礎 2. 字面量表示 3. 屬性訪問 三、SpEL核心特性詳解 1. 集合操作 2. 方法調用 3. 運算符大全 4. 類型操作 四、Sp…

算法導論第二十四章 深度學習前沿:從序列建模到創造式AI

第二十四章 深度學習前沿&#xff1a;從序列建模到創造式AI 算法的進化正在重新定義人工智能的邊界 深度學習作為機器學習領域最活躍的分支&#xff0c;正以驚人的速度推動著人工智能的發展。本章將深入探討五大前沿方向&#xff0c;通過原理分析、代碼實現和應用場景展示&…

抽象工廠設計模式

1.問題背景&#xff1a; 現在有兩個產品(Product)分別是手機殼(PhoneCase)和耳機(EarPhone)&#xff0c;但是他們會來自于各個生產廠商&#xff0c;比如說Apple和Android等等 那么至少會有四個產品&#xff0c;分別是安卓手機殼&#xff0c;安卓耳機&#xff0c;蘋果手機殼&a…

GESP 3級 C++ 知識點總結

根據GESP考試大綱 (2024年3月版)&#xff0c;幫大家總結一下GESP 3級 C語言的知識點&#xff1a; 核心目標&#xff1a; 掌握C程序的基本結構&#xff0c;理解并能運用基礎的編程概念解決稍復雜的問題&#xff0c;重點是函數、一維數組和字符串處理。 主要知識點模塊&#x…

騰訊云主動型云蜜罐技術解析:云原生威脅狩獵的革新實踐(基于騰訊云開發者社區技術網頁與行業實踐)

摘要 騰訊云主動型云蜜罐&#xff08;Active Cloud Honeypot&#xff09;通過動態誘捕機制和云原生架構&#xff0c;在威脅檢測效率、攻擊鏈還原深度、防御聯動實時性等維度實現突破。相較于傳統蜜罐&#xff0c;其核心優勢體現在&#xff1a; 部署效率&#xff1a;分鐘級完成…

企業微信wecom/jssdk的使用(入門)

概述 記錄一個企業微信jssdk的使用&#xff0c;因為要用到圖片上傳、掃描二維碼等工具。項目是uniapp開發的h5項目&#xff0c;fastadmin&#xff08;thinkphp5&#xff09;后端 先看官方文檔 https://developer.work.weixin.qq.com/document/path/90547#%E5%BA%94%E7%94%A8…

大零售生態下開源鏈動2+1模式、AI智能名片與S2B2C商城小程序的協同創新研究

摘要&#xff1a;在流量紅利消退、零售形態多元化的背景下&#xff0c;大零售生態成為商業發展的新趨勢。本文聚焦開源鏈動21模式、AI智能名片與S2B2C商城小程序在零售領域的協同應用&#xff0c;探討其如何打破傳統零售邊界&#xff0c;實現流量變現與用戶資產化。研究表明&am…

Scrapy全流程(一)

創建一個scrapy項目:scrapy startproject mySpider 生成一個爬蟲:scrapy genspider douban movie.douban.com 提取數據:完善spider&#xff0c;使用xpath等方法 保存數據:pipeline中保存數據 2 創建scrapy項目 下面以抓取豆瓣top250來學習scrapy的入門使用&#xff1a;豆瓣…

【Elasticsearch】TF-IDF 和 BM25相似性算法

在 Elasticsearch 中&#xff0c;TF-IDF 和 BM25 是兩種常用的文本相似性評分算法&#xff0c;但它們的實現和應用場景有所不同。以下是對這兩種算法的對比以及在 Elasticsearch 中的使用情況&#xff1a; TF-IDF - 定義與原理&#xff1a;TF-IDF 是一種經典的信息檢索算法&am…

【QT】控件二(輸入類控件、多元素控件、容器類控件與布局管理器)

文章目錄 1.輸入類控件1.1 LineEdit1.2 Text Edit1.3 Combo Box1.4 SpinBox1.5 Date Edit & Time Edit1.6 Dial1.7 Slider 2. 多元素控件2.1 List Widget2.2 Table Widget2.3 Tree Widget 3. 容器類控件3.1 Group Box3.2 Tab Widget 4. 布局管理器4.1 垂直布局4.2 水平布局…

【Docker基礎】Docker鏡像管理:docker pull詳解

目錄 1 Docker鏡像基礎概念 1.1 什么是Docker鏡像&#xff1f; 1.2 鏡像與容器的關系 1.3 鏡像倉庫(Registry) 2 docker pull命令詳解 2.1 基本語法 2.2 參數解釋 2.3 拉取鏡像的基本流程 2.4 鏡像分層結構解析 3 docker pull實戰指南 3.1 基本使用示例 3.2 指定鏡…

PixPin:一個強大且免費的截圖貼圖工具

PixPin 是一款國產免費的截圖工具&#xff0c;支持屏幕截圖、屏幕錄制&#xff08;GIF&#xff09;、文字識別&#xff08;OCR&#xff09;以及貼圖等功能。 高效截圖 PixPin 支持自由選擇或自動檢測窗口&#xff0c;自定義截圖區域&#xff0c;像素級精確捕捉&#xff0c;延時…

【測試報告】論壇系統

一、項目背景 1.1 測試目標及測試任務 測試目標旨在保障功能無漏洞、流程順暢&#xff0c;實現多端顯示交互一致&#xff0c;達成高并發場景下響應時間&#xff1c;2 秒等性能指標&#xff0c;抵御 SQL 注入等安全攻擊&#xff0c;提升 UI 易用性與提示友好度&#xff1b; 背…