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

一文帶你掌握C++中智能指針如何自定義刪除器

 更新時間:2026年04月14日 09:36:57   作者:OxyTheCrack  
智能指針的自定義刪除器用于管理非標準資源釋放,如C庫資源(redisFree)和非new分配的內(nèi)存等,實現(xiàn)方式主要有兩種,下面小編就和大家詳細介紹一下吧

為什么需要自定義刪除器

智能指針的核心作用是“自動管理資源”,其底層邏輯是:當智能指針對象生命周期結(jié)束時,自動調(diào)用析構(gòu)函數(shù)釋放其托管的資源。但默認的析構(gòu)邏輯是調(diào)用delete,這在很多場景下并不適用。

典型適用場景

  • C語言庫資源:比如hiredis庫的redisContext,創(chuàng)建用redisConnect(),釋放必須用redisFree(),而非delete;又如文件操作,fopen()創(chuàng)建的FILE*,必須用fclose()釋放。
  • 非new分配的內(nèi)存:用malloc/calloc分配的內(nèi)存,需要用free()釋放,無法用delete。
  • 自定義釋放邏輯:釋放資源時需要額外操作,比如關(guān)閉socket前發(fā)送終止信號、解鎖互斥鎖、注銷資源等。
  • 避免資源泄漏:如果不指定自定義刪除器,智能指針會用delete釋放非new創(chuàng)建的資源,導(dǎo)致未定義行為(崩潰、內(nèi)存泄漏)。

反面案例

以Redis連接為例,若直接使用默認智能指針,會導(dǎo)致嚴重問題:

#include <hiredis/hiredis.h>
#include <memory>

// 錯誤寫法:未指定自定義刪除器
std::unique_ptr<redisContext> ctx(redisConnect("127.0.0.1", 6379));

// 析構(gòu)時會調(diào)用delete釋放redisContext,而非redisFree()
// 后果:內(nèi)存泄漏、連接未關(guān)閉、程序可能崩潰

這就是自定義刪除器的核心價值:告訴智能指針“如何正確釋放資源”。

自定義刪除器的兩種核心實現(xiàn)方式

自定義刪除器的本質(zhì)是“給智能指針傳遞一個可調(diào)用對象”,讓智能指針在析構(gòu)時調(diào)用該對象,完成資源釋放。常用的實現(xiàn)方式有兩種:函數(shù)指針型仿函數(shù)型,二者各有優(yōu)劣,適用于不同場景。

方式一:函數(shù)指針型

將釋放資源的函數(shù)(如redisFree)作為函數(shù)指針,傳遞給智能指針,作為刪除器。

實現(xiàn)步驟

  1. 定義智能指針類型時,指定“托管類型”和“函數(shù)指針類型”(用decltype獲取函數(shù)指針類型)。
  2. 創(chuàng)建智能指針對象時,傳入“資源指針”和“刪除器函數(shù)指針”。
  3. 注意:返回空智能指針時,必須顯式傳入刪除器函數(shù)指針,否則會出現(xiàn)未定義行為。

實戰(zhàn)代碼(Redis連接池片段)

#include <hiredis/hiredis.h>
#include <memory>

// 1. 定義帶函數(shù)指針刪除器的智能指針
using redisPtr = std::unique_ptr<redisContext, decltype(&redisFree)>;

// 2. 創(chuàng)建智能指針(必須傳入刪除器)
redisContext* raw_ctx = redisConnect("127.0.0.1", 6379);
redisPtr ctx(raw_ctx, redisFree); // 正確:傳入資源指針+刪除器

// 3. 返回空智能指針(必須顯式傳入刪除器)
redisPtr getEmptyConn() {
    // 錯誤:return nullptr; (未傳入刪除器,函數(shù)指針為野指針,析構(gòu)崩潰)
    return redisPtr{nullptr, redisFree}; // 正確
}

優(yōu)缺點分析

優(yōu)點:實現(xiàn)簡單,無需額外定義類/結(jié)構(gòu)體,直接復(fù)用現(xiàn)有釋放函數(shù)。

缺點:

  • 智能指針會額外存儲函數(shù)指針,增加內(nèi)存開銷;
  • 不能直接return nullptr;
  • 函數(shù)指針無法捕獲額外上下文(如需自定義釋放邏輯,不夠靈活)。

更推薦的寫法:方式二:仿函數(shù)型

定義一個結(jié)構(gòu)體(或類),重載()運算符(仿函數(shù)),將釋放資源的邏輯寫在運算符重載函數(shù)中。這種方式是企業(yè)級開發(fā)的首選,無額外內(nèi)存開銷,且靈活安全。

實現(xiàn)步驟

  • 定義仿函數(shù)結(jié)構(gòu)體,重載()運算符,參數(shù)為“托管資源的指針”,函數(shù)體內(nèi)實現(xiàn)釋放邏輯。
  • 定義智能指針類型時,指定“托管類型”和“仿函數(shù)類型”。
  • 創(chuàng)建智能指針對象時,只需傳入資源指針,無需顯式傳入刪除器(仿函數(shù)作為類型一部分,自動綁定)。
  • 返回空智能指針時,可直接return nullptr,無需額外操作。

實戰(zhàn)代碼(Redis連接池完整片段)

#include <hiredis/hiredis.h>
#include <memory>
#include <queue>
#include <mutex>

// 1. 定義仿函數(shù)刪除器(核心:重載()運算符)
struct RedisDeleter {
    // 釋放邏輯:判斷指針非空,調(diào)用redisFree釋放
    void operator()(redisContext* ptr) const {
        if (ptr) {
            redisFree(ptr);
        }
    }
};

// 2. 定義帶仿函數(shù)刪除器的智能指針(無額外內(nèi)存開銷)
using redisPtr = std::unique_ptr<redisContext, RedisDeleter>;

// 3. Redis連接池類
class RedisConnectPool {
private:
    std::queue<redisPtr> connections_; // 隊列存智能指針,自動釋放
    std::mutex mutex_;
public:
    // 獲取連接:直接return nullptr,無需傳刪除器
    redisPtr getConnection() {
        std::lock_guard<std::mutex> lock(mutex_);
        if (connections_.empty()) {
            return nullptr; // 正確:仿函數(shù)刪除器自動綁定,無未定義行為
        }
        auto conn = std::move(connections_.front());
        connections_.pop();
        return conn;
    }

    // 歸還連接:智能指針自動管理,無需手動釋放
    void returnConnection(redisPtr conn) {
        std::lock_guard<std::mutex> lock(mutex_);
        connections_.push(std::move(conn));
    }
};

優(yōu)缺點分析

優(yōu)點:

  • 無額外內(nèi)存開銷(仿函數(shù)作為類型一部分,不占額外空間);
  • 可直接return nullptr,不易踩坑;
  • 仿函數(shù)可捕獲上下文(比如添加日志、額外釋放操作),靈活度高;
  • 類型安全,不易混用。

缺點:需要額外定義一個仿函數(shù)結(jié)構(gòu)體(代碼量略有增加,但可復(fù)用)。

避坑指南

結(jié)合筆者在Redis連接池開發(fā)中的踩坑經(jīng)驗,總結(jié)4個高頻坑點,避開這些就能寫出安全的代碼。

坑點1:仿函數(shù)重載()的返回值錯誤

自定義刪除器的仿函數(shù),()運算符必須是無返回值(void),因為智能指針調(diào)用刪除器時,不會處理返回值。若返回bool等類型,會導(dǎo)致編譯警告,甚至未定義行為。

// 錯誤寫法:返回bool
struct RedisDeleter {
    bool operator()(redisContext* ptr) { // ? 錯誤,返回值無用且有風(fēng)險
        if (ptr) redisFree(ptr);
    }
};

// 正確寫法:無返回值
struct RedisDeleter {
    void operator()(redisContext* ptr) const { // ? 正確
        if (ptr) redisFree(ptr);
    }
};

坑點2:函數(shù)指針型刪除器直接return nullptr

函數(shù)指針型刪除器的智能指針,空指針必須顯式傳入刪除器函數(shù)指針。因為智能指針需要同時初始化“資源指針”和“刪除器函數(shù)指針”,只傳nullptr會導(dǎo)致刪除器為野指針,析構(gòu)時崩潰。

// 錯誤(函數(shù)指針型)
redisPtr getEmptyConn() {
    return nullptr; // ? 未傳入刪除器,野指針崩潰
}

// 正確(函數(shù)指針型)
redisPtr getEmptyConn() {
    return redisPtr{nullptr, redisFree}; // ? 顯式傳入刪除器
}

坑點3:混用不同刪除器的智能指針

std::unique_ptr的刪除器是“類型的一部分”——不同刪除器的智能指針,是不同的類型,不能互相賦值、傳遞。

// 兩種不同刪除器的智能指針(不同類型)
using Ptr1 = std::unique_ptr<redisContext, decltype(&redisFree)>;
using Ptr2 = std::unique_ptr<redisContext, RedisDeleter>;

Ptr1 ptr1(redisConnect("127.0.0.1", 6379), redisFree);
Ptr2 ptr2 = ptr1; // ? 錯誤:類型不匹配,無法賦值

坑點4:手動調(diào)用reset()后重復(fù)釋放

智能指針的reset()方法會釋放當前托管的資源,若之后再調(diào)用pop()(隊列中)或讓智能指針生命周期結(jié)束,會導(dǎo)致雙重釋放嗎?答案是:不會。

原因:reset()釋放的是“托管的資源”,智能指針本身還活著;pop()會銷毀智能指針,此時智能指針已為空,析構(gòu)時不會再釋放資源。但注意:手動reset()是多余的,智能指針會自動釋放。

std::queue<redisPtr> q;
q.emplace(redisConnect("127.0.0.1", 6379));

// 多余但安全的寫法
q.front().reset(); // 釋放資源,智能指針變?yōu)榭?
q.pop(); // 銷毀空智能指針,無操作

// 推薦寫法(無需reset)
q.pop(); // 直接銷毀智能指針,自動釋放資源

兩種刪除器對比與選型建議

對比維度函數(shù)指針型仿函數(shù)型
內(nèi)存開銷有(存儲函數(shù)指針)無(作為類型一部分)
使用復(fù)雜度簡單(直接復(fù)用釋放函數(shù))略復(fù)雜(需定義仿函數(shù))
返回空指針需顯式傳入刪除器可直接return nullptr
靈活度低(無法捕獲上下文)高(可添加自定義邏輯)
工程推薦度低(僅適用于簡單場景)高(工業(yè)級標準寫法)

選型建議

  • 簡單場景(如單獨使用一個C庫資源,無需額外釋放邏輯):可使用函數(shù)指針型。
  • 工程開發(fā)(如連接池、工具類、長期運行的服務(wù)):優(yōu)先使用仿函數(shù)型,安全、靈活、無額外開銷。

到此這篇關(guān)于一文帶你掌握C++中智能指針如何自定義刪除器的文章就介紹到這了,更多相關(guān)C++智能指針自定義刪除器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

鹤庆县| 柳州市| 建瓯市| 滨海县| 内丘县| 吴堡县| 西充县| 当阳市| 陵川县| 响水县| 客服| 昆山市| 荣昌县| 陈巴尔虎旗| 洛宁县| 洛隆县| 嘉祥县| 筠连县| 宁陕县| 浦东新区| 富源县| 开远市| 乌兰察布市| 道孚县| 海丰县| 金溪县| 济源市| 北票市| 叶城县| 博湖县| 西畴县| 澄城县| 威信县| 梨树县| 荥经县| 万全县| 布拖县| 岢岚县| 龙州县| 吉林省| 阿图什市|