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

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

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

為什么需要自定義刪除器

智能指針的核心作用是“自動管理資源”,其底層邏輯是:當(dāng)智能指針對象生命周期結(jié)束時(shí),自動調(diào)用析構(gòu)函數(shù)釋放其托管的資源。但默認(rèn)的析構(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。
  • 自定義釋放邏輯:釋放資源時(shí)需要額外操作,比如關(guān)閉socket前發(fā)送終止信號、解鎖互斥鎖、注銷資源等。
  • 避免資源泄漏:如果不指定自定義刪除器,智能指針會用delete釋放非new創(chuàng)建的資源,導(dǎo)致未定義行為(崩潰、內(nèi)存泄漏)。

反面案例

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

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

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

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

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

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

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

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

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

實(shí)現(xiàn)步驟

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

實(shí)戰(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)缺點(diǎn)分析

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

缺點(diǎn):

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

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

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

實(shí)現(xiàn)步驟

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

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

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

// 1. 定義仿函數(shù)刪除器(核心:重載()運(yùn)算符)
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_; // 隊(duì)列存智能指針,自動釋放
    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)缺點(diǎn)分析

優(yōu)點(diǎn):

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

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

避坑指南

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

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

自定義刪除器的仿函數(shù),()運(yùn)算符必須是無返回值(void),因?yàn)橹悄苤羔樥{(diào)用刪除器時(shí),不會處理返回值。若返回bool等類型,會導(dǎo)致編譯警告,甚至未定義行為。

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

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

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

函數(shù)指針型刪除器的智能指針,空指針必須顯式傳入刪除器函數(shù)指針。因?yàn)橹悄苤羔樞枰瑫r(shí)初始化“資源指針”和“刪除器函數(shù)指針”,只傳nullptr會導(dǎo)致刪除器為野指針,析構(gòu)時(shí)崩潰。

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

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

坑點(diǎn)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; // ? 錯誤:類型不匹配,無法賦值

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

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

原因:reset()釋放的是“托管的資源”,智能指針本身還活著;pop()會銷毀智能指針,此時(shí)智能指針已為空,析構(gòu)時(shí)不會再釋放資源。但注意:手動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è)級標(biāo)準(zhǔn)寫法)

選型建議

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

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

相關(guān)文章

最新評論

张家界市| 玉林市| 芒康县| 钦州市| 苍山县| 象山县| 绍兴县| 房产| 泸溪县| 大庆市| 双鸭山市| 印江| 肇庆市| 肃宁县| 云安县| 邯郸县| 方城县| 延边| 高要市| 金阳县| 寻甸| 卢湾区| 衡阳县| 蕉岭县| 炉霍县| 石嘴山市| 漯河市| 定南县| 成都市| 边坝县| 丘北县| 小金县| 益阳市| 梁山县| 临沂市| 武夷山市| 秭归县| 锡林浩特市| 温州市| 长宁区| 泽州县|