最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++進(jìn)階異常處理與智能指針實(shí)戰(zhàn)指南

 更新時(shí)間:2025年10月09日 16:09:50   作者:會(huì)開(kāi)花的二叉樹(shù)  
本文詳解C++異常處理與智能指針,解決錯(cuò)誤傳遞冗余和資源泄漏問(wèn)題,強(qiáng)調(diào)自定義異常體系、RAII思想及智能指針類型選擇,提升代碼可靠性與可維護(hù)性,感興趣的朋友跟隨小編一起看看吧

C++ 進(jìn)階:異常處理與智能指針實(shí)戰(zhàn)指南

在 C++ 開(kāi)發(fā)中,“錯(cuò)誤處理” 與 “資源管理” 是兩大核心痛點(diǎn)。傳統(tǒng) C 語(yǔ)言的錯(cuò)誤處理方式繁瑣且脆弱,而手動(dòng)管理內(nèi)存又容易因異常導(dǎo)致泄漏。本文將從異常機(jī)制入手,逐步過(guò)渡到智能指針,帶你掌握 C++ 中可靠編程的關(guān)鍵技術(shù),解決 “如何優(yōu)雅處理錯(cuò)誤” 與 “如何安全管理資源” 兩大難題。

一、C++ 異常:告別錯(cuò)誤碼的優(yōu)雅解決方案

在 C 語(yǔ)言中,我們習(xí)慣用assert終止程序或返回錯(cuò)誤碼處理問(wèn)題,但這些方式存在明顯局限。C++ 異常機(jī)制的出現(xiàn),讓錯(cuò)誤處理更靈活、信息更完整。

1.1 先看 C 語(yǔ)言錯(cuò)誤處理的痛點(diǎn)

C 語(yǔ)言處理錯(cuò)誤的兩種核心方式,都有難以規(guī)避的缺陷:

  • 終止程序(如 assert):過(guò)于粗暴,用戶無(wú)法接受(比如內(nèi)存錯(cuò)誤直接崩潰);
  • 返回錯(cuò)誤碼:需要手動(dòng)檢查每個(gè)函數(shù)返回值,深層調(diào)用鏈中需 “層層傳遞” 錯(cuò)誤,代碼冗余且易遺漏。

舉個(gè)例子,若ConnectSql函數(shù)返回錯(cuò)誤碼,調(diào)用鏈需逐層傳遞才能讓外層處理:

// C語(yǔ)言風(fēng)格:錯(cuò)誤碼層層傳遞的冗余
int ConnectSql() {
    if (權(quán)限不足) return 1;
    if (連接失敗) return 2;
    return 0;
}
int ServerStart() {
    int ret = ConnectSql();
    if (ret != 0) return ret; // 手動(dòng)傳遞錯(cuò)誤碼
    int fd = socket();
    if (fd < 0) return errno; // 再傳遞系統(tǒng)錯(cuò)誤碼
}
int main() {
    int ret = ServerStart();
    if (ret != 0) {
        // 還需根據(jù)錯(cuò)誤碼查表判斷具體問(wèn)題
        printf("錯(cuò)誤碼:%d\n", ret);
    }
    return 0;
}

1.2 異常的基本用法:try/throw/catch

C++ 通過(guò)try(保護(hù)代碼)、throw(拋出異常)、catch(捕獲異常)三段式處理錯(cuò)誤,核心邏輯是 “哪里出錯(cuò)拋哪里,哪里能處理哪里接”。

語(yǔ)法框架
try {
    // 可能拋出異常的“保護(hù)代碼”
    函數(shù)調(diào)用或危險(xiǎn)操作;
} catch (異常類型1 e1) {
    // 處理類型1的異常
} catch (異常類型2 e2) {
    // 處理類型2的異常
} catch (...) {
    // 捕獲所有未匹配的異常(兜底,避免程序崩潰)
}
實(shí)戰(zhàn)示例:除 0 錯(cuò)誤處理

用異常重構(gòu) “除 0 錯(cuò)誤”,無(wú)需層層傳遞錯(cuò)誤碼:

#include <iostream>
using namespace std;
// 發(fā)生除0時(shí)拋出異常
double Division(int a, int b) {
    if (b == 0) {
        // 拋出字符串異常(也可拋?zhàn)远x對(duì)象)
        throw "Division by zero condition!";
    }
    return (double)a / b;
}
void Func() {
    int len, time;
    cin >> len >> time;
    // 若Division拋異常,直接跳轉(zhuǎn)到catch
    cout << Division(len, time) << endl;
}
int main() {
    try {
        Func();
    } catch (const char* errmsg) {
        // 捕獲字符串類型異常,打印錯(cuò)誤信息
        cout << "錯(cuò)誤:" << errmsg << endl;
    } catch (...) {
        // 兜底:捕獲所有其他類型異常
        cout << "未知異常" << endl;
    }
    return 0;
}

1.3 異常的核心規(guī)則:必須掌握的細(xì)節(jié)

異常的拋出與捕獲并非 “隨便匹配”,需遵守以下規(guī)則,否則易導(dǎo)致程序崩潰:

(1)匹配原則:類型決定捕獲邏輯
  • 異常對(duì)象的類型決定了哪個(gè)catch會(huì)被激活;
  • 允許 “派生類對(duì)象拋,基類捕獲”(實(shí)戰(zhàn)中常用此特性設(shè)計(jì)異常體系);
  • catch(...)是 “萬(wàn)能捕獲”,但無(wú)法獲取異常具體信息,需謹(jǐn)慎使用。
(2)棧展開(kāi):從拋出點(diǎn)找捕獲點(diǎn)

throw不在try內(nèi),或try后無(wú)匹配的catch,會(huì)觸發(fā) “棧展開(kāi)”:

  • 退出當(dāng)前函數(shù)棧,回到調(diào)用者的棧幀;
  • 重復(fù)檢查調(diào)用者的try/catch,直到找到匹配的catch;
  • 若一直找到main函數(shù)仍無(wú)匹配,程序直接終止。
(3)異常重新拋出:部分處理后移交外層

若單個(gè)catch無(wú)法完全處理異常(比如僅釋放資源),可通過(guò)throw;重新拋出,讓外層處理:

void Func() {
    // 申請(qǐng)資源(若拋異常需釋放)
    int* array = new int[10];
    try {
        int len, time;
        cin >> len >> time;
        cout << Division(len, time) << endl;
    } catch (...) {
        // 先釋放資源,再重新拋出異常
        cout << "釋放array:" << array << endl;
        delete[] array;
        throw; // 移交外層處理
    }
    // 正常執(zhí)行時(shí)釋放資源
    delete[] array;
}

1.4 異常安全:那些不能踩的坑

異常雖好,但若使用不當(dāng),會(huì)導(dǎo)致資源泄漏、對(duì)象不完整等問(wèn)題,核心注意兩點(diǎn):

(1)構(gòu)造函數(shù):盡量不拋異常

構(gòu)造函數(shù)負(fù)責(zé)對(duì)象初始化,若中途拋異常,對(duì)象可能處于 “半初始化” 狀態(tài)(部分成員已創(chuàng)建,部分未創(chuàng)建),導(dǎo)致資源泄漏。

(2)析構(gòu)函數(shù):絕對(duì)不拋異常

析構(gòu)函數(shù)負(fù)責(zé)資源清理(如delete、關(guān)閉文件),若拋異常:

  • 若已有一個(gè)異常在處理中,會(huì)直接終止程序;
  • 可能導(dǎo)致資源未釋放(如delete未執(zhí)行)。

1.5 實(shí)戰(zhàn):自定義異常體系(企業(yè)級(jí)規(guī)范)

實(shí)際項(xiàng)目中,若隨意拋int、string等零散類型,外層無(wú)法統(tǒng)一處理。規(guī)范做法是設(shè)計(jì)繼承式異常體系:定義一個(gè)基類,所有具體異常繼承自它,外層只需捕獲基類即可。

企業(yè)級(jí)異常體系示例
#include <string>
using namespace std;
// 異?;?
class Exception {
public:
    Exception(const string& errmsg, int id) 
        : _errmsg(errmsg), _id(id) {}
    // 虛函數(shù):支持多態(tài),子類重寫錯(cuò)誤信息
    virtual string what() const {
        return _errmsg;
    }
protected:
    string _errmsg; // 錯(cuò)誤描述
    int _id;        // 錯(cuò)誤編號(hào)(便于定位問(wèn)題)
};
// SQL異常(派生類)
class SqlException : public Exception {
public:
    SqlException(const string& errmsg, int id, const string& sql)
        : Exception(errmsg, id), _sql(sql) {}
    // 重寫what,添加SQL信息
    virtual string what() const override {
        string str = "SqlException: ";
        str += _errmsg;
        str += " (SQL: ";
        str += _sql;
        str += ")";
        return str;
    }
private:
    string _sql; // 出問(wèn)題的SQL語(yǔ)句
};
// 緩存異常(派生類)
class CacheException : public Exception {
public:
    CacheException(const string& errmsg, int id)
        : Exception(errmsg, id) {}
    virtual string what() const override {
        return "CacheException: " + _errmsg;
    }
};
// 模擬SQL操作:隨機(jī)拋異常
void SQLMgr() {
    srand(time(0));
    if (rand() % 7 == 0) {
        throw SqlException("權(quán)限不足", 100, "select * from user where name='張三'");
    }
}
// 模擬緩存操作:隨機(jī)拋異常
void CacheMgr() {
    srand(time(0));
    if (rand() % 5 == 0) {
        throw CacheException("數(shù)據(jù)不存在", 101);
    }
    SQLMgr(); // 調(diào)用SQL操作
}
int main() {
    while (1) {
        this_thread::sleep_for(chrono::seconds(1));
        try {
            CacheMgr();
            cout << "操作成功" << endl;
        } catch (const Exception& e) {
            // 捕獲基類,統(tǒng)一處理所有異常(多態(tài)生效)
            cout << "捕獲異常:" << e.what() << endl;
        } catch (...) {
            cout << "未知異常" << endl;
        }
    }
    return 0;
}
優(yōu)勢(shì)
  • 外層只需一個(gè)catch(const Exception& e),即可處理所有派生類異常;
  • 錯(cuò)誤信息結(jié)構(gòu)化(含錯(cuò)誤編號(hào)、SQL 語(yǔ)句等),便于定位問(wèn)題;
  • 擴(kuò)展性強(qiáng):新增異常類型只需繼承基類,無(wú)需修改外層捕獲邏輯。

1.6 標(biāo)準(zhǔn)庫(kù)異常體系:了解即可

C++ 標(biāo)準(zhǔn)庫(kù)定義了一套異常體系(頭文件<exception>),所有異常繼承自std::exception,但實(shí)際中很少直接使用 —— 因?yàn)樵O(shè)計(jì)較簡(jiǎn)單,無(wú)法滿足復(fù)雜業(yè)務(wù)需求(如無(wú)法攜帶錯(cuò)誤編號(hào)、SQL 語(yǔ)句等)。

核心繼承關(guān)系:

std::exception
├─ std::bad_alloc(new失敗時(shí)拋)
├─ std::bad_cast(dynamic_cast失敗時(shí)拋)
├─ std::logic_error(邏輯錯(cuò)誤,如參數(shù)無(wú)效)
│  ├─ std::invalid_argument(無(wú)效參數(shù))
│  └─ std::out_of_range(越界,如vector::at)
└─ std::runtime_error(運(yùn)行時(shí)錯(cuò)誤)
   └─ std::overflow_error(算術(shù)溢出)

使用示例(捕獲vector越界異常):

#include <vector>
#include <exception>
int main() {
    try {
        vector<int> v(10);
        v.at(10) = 100; // 越界,拋out_of_range
    } catch (const exception& e) {
        // 調(diào)用what()獲取錯(cuò)誤信息
        cout << e.what() << endl; // 輸出:vector::_M_range_check: __n (which is 10) >= this->size() (which is 10)
    }
    return 0;
}

1.7 異常的優(yōu)缺點(diǎn)總結(jié)

優(yōu)點(diǎn)缺點(diǎn)
錯(cuò)誤信息完整(可攜帶上下文,如 SQL 語(yǔ)句)執(zhí)行流跳轉(zhuǎn)混亂,調(diào)試難度增加
無(wú)需層層傳遞錯(cuò)誤碼,代碼更簡(jiǎn)潔存在輕微性能開(kāi)銷(現(xiàn)代硬件可忽略)
支持構(gòu)造函數(shù) / 運(yùn)算符重載等無(wú)返回值場(chǎng)景易導(dǎo)致資源泄漏(需配合智能指針解決)
兼容第三方庫(kù)(如 boost、gtest)標(biāo)準(zhǔn)庫(kù)異常體系不實(shí)用,需自定義

結(jié)論:異常利大于弊,是 C++ 錯(cuò)誤處理的主流方案,關(guān)鍵是配合智能指針解決資源泄漏問(wèn)題。

二、智能指針:解決異常資源泄漏的 “神器”

異常會(huì)導(dǎo)致代碼執(zhí)行流跳變,若new后拋異常,delete可能無(wú)法執(zhí)行,進(jìn)而引發(fā)內(nèi)存泄漏。智能指針的出現(xiàn),正是通過(guò)RAII 思想,讓資源自動(dòng)釋放。

2.1 先看異常引發(fā)的隱患:內(nèi)存泄漏

以下代碼中,若Func()拋異常,delete p會(huì)被跳過(guò),導(dǎo)致內(nèi)存泄漏:

void Func() {
    throw "模擬異常"; // 拋出異常
}
void Test() {
    int* p = new int; // 申請(qǐng)內(nèi)存
    Func();           // 拋異常,執(zhí)行流跳走
    delete p;         // 永遠(yuǎn)不會(huì)執(zhí)行,內(nèi)存泄漏
}

內(nèi)存泄漏定義:程序分配內(nèi)存后,因設(shè)計(jì)錯(cuò)誤失去對(duì)該內(nèi)存的控制,導(dǎo)致內(nèi)存無(wú)法復(fù)用,長(zhǎng)期運(yùn)行會(huì)使程序響應(yīng)變慢甚至卡死。

2.2 RAII 思想:智能指針的基石

RAII(Resource Acquisition Is Initialization),即 “資源獲取即初始化”,核心邏輯是:

  • 構(gòu)造時(shí)獲取資源:將資源(如內(nèi)存、文件句柄)綁定到對(duì)象的生命周期;
  • 析構(gòu)時(shí)釋放資源:對(duì)象銷毀時(shí),析構(gòu)函數(shù)自動(dòng)釋放資源,無(wú)需手動(dòng)調(diào)用。
基于 RAII 的簡(jiǎn)單智能指針
template<class T>
class SmartPtr {
public:
    // 構(gòu)造:獲取資源(內(nèi)存)
    SmartPtr(T* ptr = nullptr) : _ptr(ptr) {}
    // 析構(gòu):釋放資源(內(nèi)存)
    ~SmartPtr() {
        if (_ptr) {
            delete _ptr;
            cout << "釋放內(nèi)存:" << _ptr << endl;
        }
    }
    // 重載*和->,讓SmartPtr像普通指針一樣使用
    T& operator*() { return *_ptr; }
    T* operator->() { return _ptr; }
private:
    T* _ptr; // 管理的資源(內(nèi)存地址)
};
// 測(cè)試:即使拋異常,內(nèi)存也會(huì)自動(dòng)釋放
void Test() {
    SmartPtr<int> sp(new int); // 構(gòu)造:獲取內(nèi)存
    *sp = 10;                  // 像普通指針一樣使用
    throw "模擬異常";           // 拋異常,sp對(duì)象銷毀時(shí)調(diào)用析構(gòu)
    // 無(wú)需手動(dòng)delete,析構(gòu)自動(dòng)釋放
}
核心優(yōu)勢(shì)
  • 無(wú)需顯式釋放資源,避免人為遺漏;
  • 即使發(fā)生異常,對(duì)象也會(huì)被銷毀(棧對(duì)象在棧展開(kāi)時(shí)自動(dòng)析構(gòu)),資源必然釋放。

三、C++ 智能指針全家桶:原理與實(shí)戰(zhàn)

C++ 標(biāo)準(zhǔn)庫(kù)提供了 4 種智能指針,分別應(yīng)對(duì)不同場(chǎng)景,其中auto_ptr已被淘汰,重點(diǎn)掌握unique_ptr、shared_ptrweak_ptr。

3.1 auto_ptr:失敗的早期嘗試(避坑)

auto_ptr是 C++98 提供的第一個(gè)智能指針,采用 “管理權(quán)轉(zhuǎn)移” 機(jī)制,但存在嚴(yán)重缺陷,企業(yè)中已明確禁止使用

缺陷:管理權(quán)轉(zhuǎn)移導(dǎo)致懸空指針

當(dāng)auto_ptr對(duì)象拷貝或賦值時(shí),會(huì)轉(zhuǎn)移資源的管理權(quán),原對(duì)象變?yōu)?“懸空指針”(指向 nullptr),訪問(wèn)原對(duì)象會(huì)崩潰:

#include <memory> // auto_ptr所在頭文件
int main() {
    auto_ptr<int> sp1(new int(10));
    auto_ptr<int> sp2 = sp1; // 管理權(quán)轉(zhuǎn)移:sp1失去資源,sp2擁有資源
    *sp2 = 20; // 正常:sp2擁有資源
    *sp1 = 30; // 崩潰:sp1已懸空(指向nullptr)
    return 0;
}

結(jié)論:永遠(yuǎn)不要使用auto_ptr,改用unique_ptr

3.2 unique_ptr:獨(dú)占所有權(quán)的高效選擇

unique_ptr是 C++11 替代auto_ptr的方案,核心是獨(dú)占資源所有權(quán)—— 同一時(shí)間,只有一個(gè)unique_ptr能管理資源,禁止拷貝和賦值(直接刪除拷貝構(gòu)造和賦值運(yùn)算符)。

核心特性
  • 禁止拷貝:unique_ptr(const unique_ptr&) = delete;
  • 禁止賦值:unique_ptr& operator=(const unique_ptr&) = delete;
  • 支持移動(dòng)語(yǔ)義:可通過(guò)std::move轉(zhuǎn)移所有權(quán)(轉(zhuǎn)移后原對(duì)象懸空)。
實(shí)戰(zhàn)示例
#include <memory>
int main() {
    // 1. 基本使用
    unique_ptr<int> sp1(new int(10));
    cout << *sp1 << endl; // 10
    // 2. 禁止拷貝和賦值(編譯報(bào)錯(cuò))
    // unique_ptr<int> sp2 = sp1; // 錯(cuò)誤:拷貝構(gòu)造已刪除
    // sp1 = sp2;                // 錯(cuò)誤:賦值運(yùn)算符已刪除
    // 3. 移動(dòng)語(yǔ)義:轉(zhuǎn)移所有權(quán)
    unique_ptr<int> sp2 = std::move(sp1); // 轉(zhuǎn)移后sp1懸空
    cout << *sp2 << endl; // 10
    // cout << *sp1 << endl; // 崩潰:sp1已懸空
    // 4. 管理數(shù)組(需指定刪除器,或用unique_ptr<int[]>)
    unique_ptr<int[]> sp3(new int[5]); // 專門用于數(shù)組,析構(gòu)時(shí)調(diào)用delete[]
    sp3[0] = 1;
    sp3[1] = 2;
    return 0;
}
適用場(chǎng)景
  • 資源僅需一個(gè)所有者(如局部變量、函數(shù)返回值);
  • 追求高效(無(wú)引用計(jì)數(shù)開(kāi)銷,性能接近普通指針)。

3.3 shared_ptr:共享所有權(quán)的靈活方案

unique_ptr不支持拷貝,無(wú)法滿足 “多對(duì)象共享資源” 的場(chǎng)景(如多線程共享數(shù)據(jù))。shared_ptr通過(guò)引用計(jì)數(shù)實(shí)現(xiàn)共享所有權(quán),核心是 “記錄資源被多少對(duì)象引用,最后一個(gè)對(duì)象銷毀時(shí)釋放資源”。

核心原理
  • 每個(gè)shared_ptr管理一個(gè)資源和一個(gè) “引用計(jì)數(shù)”(記錄共享該資源的shared_ptr數(shù)量);
  • 拷貝shared_ptr時(shí),引用計(jì)數(shù) + 1;
  • shared_ptr銷毀時(shí),引用計(jì)數(shù) - 1;
  • 若引用計(jì)數(shù)變?yōu)?0,釋放資源。
模擬實(shí)現(xiàn)核心代碼
template<class T>
class shared_ptr {
public:
    // 構(gòu)造:資源+引用計(jì)數(shù)(初始為1)
    shared_ptr(T* ptr = nullptr) 
        : _ptr(ptr), _pRefCount(new int(1)), _pmtx(new mutex) {}
    // 拷貝構(gòu)造:引用計(jì)數(shù)+1
    shared_ptr(const shared_ptr<T>& sp) 
        : _ptr(sp._ptr), _pRefCount(sp._pRefCount), _pmtx(sp._pmtx) {
        AddRef(); // 引用計(jì)數(shù)+1(加鎖保證線程安全)
    }
    // 賦值運(yùn)算符:釋放當(dāng)前資源,引用新資源
    shared_ptr<T>& operator=(const shared_ptr<T>& sp) {
        if (_ptr != sp._ptr) { // 避免自賦值
            Release(); // 釋放當(dāng)前資源(引用計(jì)數(shù)-1,為0則刪除)
            _ptr = sp._ptr;
            _pRefCount = sp._pRefCount;
            _pmtx = sp._pmtx;
            AddRef(); // 新資源引用計(jì)數(shù)+1
        }
        return *this;
    }
    // 析構(gòu):引用計(jì)數(shù)-1,為0則釋放資源
    ~shared_ptr() {
        Release();
    }
    // 重載*和->
    T& operator*() { return *_ptr; }
    T* operator->() { return _ptr; }
    // 獲取引用計(jì)數(shù)
    int use_count() const { return *_pRefCount; }
private:
    // 引用計(jì)數(shù)+1(加鎖,線程安全)
    void AddRef() {
        _pmtx->lock();
        (*_pRefCount)++;
        _pmtx->unlock();
    }
    // 引用計(jì)數(shù)-1,為0則釋放資源
    void Release() {
        _pmtx->lock();
        bool needDelete = false;
        if (--(*_pRefCount) == 0) {
            delete _ptr;
            delete _pRefCount;
            needDelete = true;
        }
        _pmtx->unlock();
        if (needDelete) {
            delete _pmtx; // 最后一個(gè)對(duì)象銷毀時(shí),刪除鎖
        }
    }
private:
    T* _ptr;          // 管理的資源
    int* _pRefCount;  // 引用計(jì)數(shù)(指針:所有共享對(duì)象共享同一計(jì)數(shù))
    mutex* _pmtx;     // 互斥鎖:保證引用計(jì)數(shù)操作線程安全
};
實(shí)戰(zhàn)要點(diǎn)
  • 線程安全
    • 引用計(jì)數(shù)的加減是線程安全的(內(nèi)部加鎖);
    • 資源本身的訪問(wèn)不是線程安全的(需用戶手動(dòng)加鎖)。
  • 自定義刪除器
    • shared_ptr默認(rèn)用delete釋放資源,若資源是malloc分配的、數(shù)組或文件句柄,需自定義刪除器:
#include <cstdlib> // malloc/free
#include <cstdio>  // FILE/fclose
// 1. 管理malloc分配的內(nèi)存(自定義刪除器)
void FreeFunc(int* ptr) {
    free(ptr);
    cout << "free內(nèi)存:" << ptr << endl;
}
shared_ptr<int> sp1((int*)malloc(4), FreeFunc);
// 2. 管理數(shù)組(用lambda作為刪除器)
shared_ptr<int> sp2(new int[5], [](int* ptr) {
    delete[] ptr;
    cout << "delete[]數(shù)組:" << ptr << endl;
});
// 3. 管理文件句柄
shared_ptr<FILE> sp3(fopen("test.txt", "w"), [](FILE* ptr) {
    fclose(ptr);
    cout << "關(guān)閉文件:" << ptr << endl;
});

3.4 weak_ptr:破解 shared_ptr 循環(huán)引用

shared_ptr存在一個(gè)致命問(wèn)題:循環(huán)引用—— 兩個(gè)shared_ptr互相引用,導(dǎo)致引用計(jì)數(shù)無(wú)法歸零,資源永遠(yuǎn)無(wú)法釋放。

問(wèn)題示例:雙向鏈表節(jié)點(diǎn)
struct ListNode {
    int _data;
    shared_ptr<ListNode> _prev; // 指向前驅(qū)節(jié)點(diǎn)
    shared_ptr<ListNode> _next; // 指向后繼節(jié)點(diǎn)
    ~ListNode() { cout << "~ListNode()" << endl; }
};
int main() {
    shared_ptr<ListNode> node1(new ListNode);
    shared_ptr<ListNode> node2(new ListNode);
    cout << node1.use_count() << endl; // 1
    cout << node2.use_count() << endl; // 1
    node1->_next = node2; // node1的_next引用node2,node2計(jì)數(shù)變?yōu)?
    node2->_prev = node1; // node2的_prev引用node1,node1計(jì)數(shù)變?yōu)?
    // 析構(gòu)node1和node2:計(jì)數(shù)各減1,變?yōu)?(非0),資源不釋放
    return 0;
}
循環(huán)引用分析
  • node1node2析構(gòu)時(shí),引用計(jì)數(shù)從 2 減到 1(因_next_prev仍互相引用);
  • 只有_next_prev析構(gòu)時(shí),計(jì)數(shù)才會(huì)減到 0,但_next屬于node1,node1不釋放則_next不析構(gòu);
  • 最終形成 “死鎖”,資源永遠(yuǎn)無(wú)法釋放。
解決方案:用 weak_ptr 打破循環(huán)

weak_ptr是 “弱引用” 智能指針,特點(diǎn)是:

  • 不增加引用計(jì)數(shù),僅觀察資源;
  • 無(wú)法直接訪問(wèn)資源(需先通過(guò)lock()轉(zhuǎn)為shared_ptr)。

修改鏈表節(jié)點(diǎn),將_prev_next改為weak_ptr

#include <memory>
struct ListNode {
    int _data;
    weak_ptr<ListNode> _prev;  // 弱引用:不增加計(jì)數(shù)
    weak_ptr<ListNode> _next;  // 弱引用:不增加計(jì)數(shù)
    ~ListNode() { cout << "~ListNode()" << endl; }
};
int main() {
    shared_ptr<ListNode> node1(new ListNode);
    shared_ptr<ListNode> node2(new ListNode);
    cout << node1.use_count() << endl; // 1
    cout << node2.use_count() << endl; // 1
    node1->_next = node2; // weak_ptr賦值,node2計(jì)數(shù)仍為1
    node2->_prev = node1; // weak_ptr賦值,node1計(jì)數(shù)仍為1
    // 析構(gòu)node1和node2:計(jì)數(shù)減到0,資源釋放(打印~ListNode())
    return 0;
}
weak_ptr 的使用場(chǎng)景
  • 打破shared_ptr的循環(huán)引用(如雙向鏈表、樹(shù)結(jié)構(gòu));
  • 觀察資源是否存在(通過(guò)lock()判斷:若資源存在,返回非空shared_ptr;否則返回空)。

四、C++11 與 boost 智能指針的淵源

C++11 的智能指針并非憑空出現(xiàn),而是借鑒了boost庫(kù)的設(shè)計(jì):

  • C++98:僅提供auto_ptr,設(shè)計(jì)缺陷明顯;
  • boost 庫(kù):提出scoped_ptr(獨(dú)占)、shared_ptr(共享)、weak_ptr(弱引用),解決了auto_ptr的問(wèn)題;
  • C++ TR1:引入shared_ptr,但非標(biāo)準(zhǔn);
  • C++11:正式納入unique_ptr(對(duì)應(yīng)boost::scoped_ptr)、shared_ptrweak_ptr,并優(yōu)化實(shí)現(xiàn)。

結(jié)論:C++11 智能指針是boost智能指針的 “標(biāo)準(zhǔn)化版本”,兼容性更好,無(wú)需額外依賴boost庫(kù)。

五、總結(jié):異常與智能指針的最佳實(shí)踐

  1. 異常使用規(guī)范
  • 定義繼承式異常體系,所有異常繼承自同一基類;
  • 構(gòu)造函數(shù)盡量不拋異常,析構(gòu)函數(shù)絕對(duì)不拋異常;
  • 外層用catch(...)兜底,避免程序崩潰。
  1. 智能指針選擇優(yōu)先級(jí)
  • 優(yōu)先用unique_ptr(獨(dú)占資源,高效無(wú)開(kāi)銷);
  • 需共享資源時(shí)用shared_ptr(注意循環(huán)引用,用weak_ptr解決);
  • 永遠(yuǎn)不用auto_ptr。
  1. 資源管理原則
  • 內(nèi)存、文件句柄等資源,優(yōu)先用智能指針管理;
  • 自定義資源(如網(wǎng)絡(luò)連接),用 RAII 思想封裝成類,讓資源自動(dòng)釋放。

通過(guò) “異常處理錯(cuò)誤”+“智能指針管理資源”,可大幅提升 C++ 程序的可靠性和可維護(hù)性,這也是企業(yè)級(jí) C++ 開(kāi)發(fā)的核心技術(shù)之一。

到此這篇關(guān)于C++異常處理與智能指針實(shí)戰(zhàn)指南的文章就介紹到這了,更多相關(guān)C++異常與智能指針內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++中static修飾符的詳解及其作用介紹

    C++中static修飾符的詳解及其作用介紹

    這篇文章主要介紹了C++中static修飾符的詳解及其作用介紹,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • C語(yǔ)言深入講解函數(shù)參數(shù)的使用

    C語(yǔ)言深入講解函數(shù)參數(shù)的使用

    函數(shù)的參數(shù)分為形參和實(shí)參兩種。形參出現(xiàn)在函數(shù)定義中,在整個(gè)函數(shù)體內(nèi)都可以使用,離開(kāi)該函數(shù)則不能使用。實(shí)參出現(xiàn)在主調(diào)函數(shù)中,進(jìn)入被調(diào)函數(shù)后,實(shí)參變量也不能使用
    2022-04-04
  • C語(yǔ)言+win32api寫窗體應(yīng)用程序

    C語(yǔ)言+win32api寫窗體應(yīng)用程序

    本文給大家分享的是個(gè)人使用純C語(yǔ)言結(jié)合win32api制作窗體應(yīng)用程序的代碼,非常的簡(jiǎn)單,給需要的小伙伴參考下。
    2016-02-02
  • 數(shù)據(jù)結(jié)構(gòu) 數(shù)組順序存儲(chǔ)詳細(xì)介紹

    數(shù)據(jù)結(jié)構(gòu) 數(shù)組順序存儲(chǔ)詳細(xì)介紹

    這篇文章主要介紹了數(shù)據(jù)結(jié)構(gòu) 數(shù)組順序存儲(chǔ)詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • C++?繼承的范例講解

    C++?繼承的范例講解

    繼承是C++面向?qū)ο缶幊讨械囊婚T。繼承是子類繼承父類的特征和行為,或者是繼承父類得方法,使的子類具有父類得的特性和行為。重寫是子類對(duì)父類的允許訪問(wèn)的方法實(shí)行的過(guò)程進(jìn)行重新編寫,返回值和形參都不能改變。就是對(duì)原本的父類進(jìn)行重新編寫,但是外部接口不能被重寫
    2022-06-06
  • C語(yǔ)言可變長(zhǎng)的參數(shù)列表詳解

    C語(yǔ)言可變長(zhǎng)的參數(shù)列表詳解

    這篇文章主要為大家介紹了C語(yǔ)言可變長(zhǎng)的參數(shù)列表,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-01-01
  • 利用QT實(shí)現(xiàn)圖片瀏覽器的示例詳解

    利用QT實(shí)現(xiàn)圖片瀏覽器的示例詳解

    這篇文章主要和大家分享一個(gè)小案例:利用QT制作一個(gè)小的圖片瀏覽器,要求可以顯示jpg、jpeg、png、bmp,還可以從電腦上拖動(dòng)圖到窗口并顯示出來(lái),感興趣的可以了解一下
    2023-02-02
  • Cocos2d-x UI開(kāi)發(fā)之CCControlSwitch控件類使用實(shí)例

    Cocos2d-x UI開(kāi)發(fā)之CCControlSwitch控件類使用實(shí)例

    這篇文章主要介紹了Cocos2d-x UI開(kāi)發(fā)之CCControlSwitch控件類使用實(shí)例,本文代碼中含大量注釋講解了CCControlSwitch控件類的使用,需要的朋友可以參考下
    2014-09-09
  • C++中析構(gòu)函數(shù)為何是虛函數(shù)

    C++中析構(gòu)函數(shù)為何是虛函數(shù)

    這篇文章主要介紹了C++中析構(gòu)函數(shù)為何是虛函數(shù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • vs2019+win10配置boost庫(kù)的詳細(xì)教程

    vs2019+win10配置boost庫(kù)的詳細(xì)教程

    這篇文章主要介紹了vs2019+win10配置boost庫(kù),本文通過(guò)圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-06-06

最新評(píng)論

景东| 且末县| 新余市| 上蔡县| 宣城市| 鹰潭市| 长汀县| 苗栗县| 苍山县| 绥中县| 社旗县| 抚州市| 酉阳| 长顺县| 宜君县| 紫阳县| 偃师市| 逊克县| 朝阳县| 铅山县| 大庆市| 湘潭市| 山阴县| 瓦房店市| 灌南县| 水富县| 中卫市| 承德市| 宁海县| 新郑市| 自治县| 永胜县| 威远县| 惠来县| 延川县| 班玛县| 灵丘县| 大悟县| 济南市| 喀喇沁旗| 岫岩|