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

C++ 智能指針原理、使用與最佳實(shí)踐指南

 更新時(shí)間:2025年11月15日 10:59:17   作者:2401_87727424  
本文全面介紹了C++智能指針的使用,包括RAII思想、標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)、核心原理和實(shí)際問題解決,重點(diǎn)講解了unique_ptr、shared_ptr和weak_ptr的使用場(chǎng)景、優(yōu)缺點(diǎn)及特殊資源管理,感興趣的朋友跟隨小編一起看看吧

前言

在 C++ 編程中,動(dòng)態(tài)內(nèi)存管理是核心難點(diǎn)之一,手動(dòng)new/delete容易因異常處理、邏輯疏忽導(dǎo)致內(nèi)存泄漏。智能指針作為 RAII 思想的典型實(shí)現(xiàn),完美解決了這一問題。本文將結(jié)合完整代碼示例,從使用場(chǎng)景、設(shè)計(jì)思路、標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)、核心原理到實(shí)際問題解決,全面講解智能指針的相關(guān)知識(shí)。

一、智能指針的核心使用場(chǎng)景

手動(dòng)管理動(dòng)態(tài)內(nèi)存時(shí),異常會(huì)打斷程序執(zhí)行流程,導(dǎo)致后續(xù)delete語(yǔ)句無(wú)法執(zhí)行,最終造成內(nèi)存泄漏。以下是典型教學(xué)代碼:

double Divide(int a, int b) {
    if (b == 0)
        throw "Divide by zero condition!"; // 除0拋出異常
    else
        return (double)a / (double)b;
}
void Func() {
    int* array1 = new int[10];
    int* array2 = new int[10]; // 若此處拋異常,array1已無(wú)法釋放
    try {
        int len, time;
        cin >> len >> time;
        cout << Divide(len, time) << endl;
    }
    catch (...) {
        // 捕獲異常后釋放資源,再重新拋出
        cout << "delete []" << array1 << endl;
        cout << "delete []" << array2 << endl;
        delete[] array1;
        delete[] array2;
        throw;
    }
    // 正常流程釋放資源
    cout << "delete []" << array1 << endl;
    delete[] array1;
    cout << "delete []" << array2 << endl;
    delete[] array2;
}
int main() {
    try {
        Func();
    }
    catch (const char* errmsg) {
        cout << errmsg << endl;
    }
    catch (const exception& e) {
        cout << e.what() << endl;
    }
    catch (...) {
        cout << "未知異常" << endl;
    }
    return 0;
}

痛點(diǎn)分析:為覆蓋所有異常場(chǎng)景,需要嵌套多層try-catch,代碼冗余且易出錯(cuò)。

二、RAII 思想與智能指針設(shè)計(jì)思路

2.1 RAII 核心思想

RAII(Resource Acquisition Is Initialization)即 “資源獲取即初始化”,本質(zhì)是一種利用對(duì)象生命周期來(lái)管理獲取到的動(dòng)態(tài)資源,避免資源泄漏的資源管理機(jī)制。

  • 資源(內(nèi)存、文件句柄、網(wǎng)絡(luò)連接等)獲取時(shí),委托給一個(gè)對(duì)象管理。
  • 資源在對(duì)象生命周期內(nèi)始終有效。
  • 對(duì)象析構(gòu)時(shí)自動(dòng)釋放資源,確保資源不會(huì)泄漏。

2.2 智能指針的設(shè)計(jì)要點(diǎn)

智能指針需滿足兩個(gè)核心需求:遵循 RAII 思想、模擬原生指針行為。以下是簡(jiǎn)化的智能指針實(shí)現(xiàn):

template<class T>
class SmartPtr {
public:
    // RAII:構(gòu)造時(shí)接管資源
    SmartPtr(T* ptr) : _ptr(ptr) {}
    // 析構(gòu)時(shí)自動(dòng)釋放資源
    ~SmartPtr() {
        cout << "delete[] " << _ptr << endl;
        delete[] _ptr;
    }
    // 重載運(yùn)算符,模擬指針行為
    T& operator*() { return *_ptr; }
    T* operator->() { return _ptr; }
    T& operator[](size_t i) { return _ptr[i]; }
private:
    T* _ptr; // 管理的資源指針
};

2.3 優(yōu)化后的 Func 函數(shù)

使用自定義SmartPtr后,無(wú)需手動(dòng)釋放資源,異常場(chǎng)景下也能自動(dòng)析構(gòu):

double Divide(int a, int b) {
    if (b == 0)
        throw "Divide by zero condition!";
    else
        return (double)a / (double)b;
}
void Func() {
    // 智能指針管理動(dòng)態(tài)數(shù)組,自動(dòng)釋放
    SmartPtr<int> sp1 = new int[10];
    SmartPtr<int> sp2 = new int[10];
    for (size_t i = 0; i < 10; i++)
        sp1[i] = sp2[i] = i; // 重載[]運(yùn)算符,直接訪問
    int len, time;
    cin >> len >> time;
    cout << Divide(len, time) << endl;
}
int main() {
    try {
        Func();
    }
    catch (const char* errmsg) {
        cout << errmsg << endl;
    }
    catch (const exception& e) {
        cout << e.what() << endl;
    }
    catch (...) {
        cout << "未知異常" << endl;
    }
    return 0;
}

三、C++ 標(biāo)準(zhǔn)庫(kù)智能指針的使用

C++ 標(biāo)準(zhǔn)庫(kù)提供的智能指針均定義在<memory>頭文件中,核心包括auto_ptr(已廢棄)、unique_ptr、shared_ptr、weak_ptr,它們的核心差異在于資源所有權(quán)管理機(jī)制。

3.1 各智能指針核心特性對(duì)比

智能指針推出標(biāo)準(zhǔn)核心特性適用場(chǎng)景
auto_ptrC++98拷貝時(shí)轉(zhuǎn)移資源所有權(quán)已廢棄,避免使用
unique_ptrC++11獨(dú)占資源,禁止拷貝,支持移動(dòng)無(wú)需共享資源的場(chǎng)景
shared_ptrC++11共享資源,引用計(jì)數(shù)實(shí)現(xiàn)需要拷貝 / 共享資源的場(chǎng)景
weak_ptrC++11不管理資源,不增加引用計(jì)數(shù)解決shared_ptr循環(huán)引用

3.2 基礎(chǔ)使用示例

首先定義測(cè)試類Date,用于驗(yàn)證析構(gòu)行為:

struct Date {
    int _year;
    int _month;
    int _day;
    Date(int year = 1, int month = 1, int day = 1)
        : _year(year), _month(month), _day(day) {}
    ~Date() {
        cout << "~Date()" << endl;
    }
};

3.2.1auto_ptr(廢棄)

auto_ptr的拷貝會(huì)轉(zhuǎn)移資源所有權(quán),導(dǎo)致原對(duì)象懸空,訪問時(shí)會(huì)崩潰:

int main() {
    auto_ptr<Date> ap1(new Date);
    auto_ptr<Date> ap2(ap1); // 拷貝后,ap1的資源所有權(quán)轉(zhuǎn)移給ap2
    // ap1->_year++; // 錯(cuò)誤:ap1已懸空,空指針訪問
    return 0;
}

3.2.2unique_ptr(獨(dú)占所有權(quán))

unique_ptr禁止拷貝,支持移動(dòng)語(yǔ)義(移動(dòng)后原對(duì)象懸空,需謹(jǐn)慎使用):

int main() {
    unique_ptr<Date> up1(new Date);
    // unique_ptr<Date> up2(up1); // 錯(cuò)誤:禁止拷貝
    unique_ptr<Date> up3(move(up1)); // 支持移動(dòng),up1懸空
    return 0;
}

3.2.3shared_ptr(共享所有權(quán))

shared_ptr通過引用計(jì)數(shù)跟蹤資源持有者數(shù)量,拷貝時(shí)計(jì)數(shù)遞增,析構(gòu)時(shí)計(jì)數(shù)遞減,計(jì)數(shù)為 0 時(shí)釋放資源:

int main() {
    shared_ptr<Date> sp1(new Date);
    shared_ptr<Date> sp2(sp1); // 拷貝,引用計(jì)數(shù)變?yōu)?
    shared_ptr<Date> sp3(sp2); // 拷貝,引用計(jì)數(shù)變?yōu)?
    cout << sp1.use_count() << endl; // 輸出3:查看引用計(jì)數(shù)
    sp1->_year++; // 重載->,直接訪問成員
    cout << sp1->_year << endl; // 輸出2
    cout << sp2->_year << endl; // 輸出2(共享同一資源)
    cout << sp3->_year << endl; // 輸出2
    shared_ptr<Date> sp4(move(sp1)); // 移動(dòng),sp1懸空
    return 0;
}

3.3 特殊資源管理:刪除器

智能指針默認(rèn)使用delete釋放資源,若管理new[]分配的數(shù)組或文件句柄等特殊資源,需自定義刪除器(可調(diào)用對(duì)象)。

3.3.1 管理new[]數(shù)組

unique_ptrshared_ptr提供了new[]特化版本,直接支持?jǐn)?shù)組管理:

int main() {
    // 特化版本,析構(gòu)時(shí)自動(dòng)調(diào)用delete[]
    unique_ptr<Date[]> up1(new Date[5]);
    shared_ptr<Date[]> sp1(new Date[5]);
    return 0;
}

重點(diǎn): 自定義刪除器(三種方式)

此處提供仿函數(shù)、函數(shù)指針、lambda 表達(dá)式三種刪除器實(shí)現(xiàn):

// 1. 函數(shù)指針刪除器
template<class T>
void DeleteArrayFunc(T* ptr) {
    delete[] ptr;
}
// 2. 仿函數(shù)刪除器
template<class T>
class DeleteArray {
public:
    void operator()(T* ptr) {
        delete[] ptr;
    }
};
// 3. 文件句柄刪除器(仿函數(shù))
class Fclose {
public:
    void operator()(FILE* ptr) {
        cout << "fclose:" << ptr << endl;
        fclose(ptr);
    }
};
int main() {
    // 仿函數(shù)作為刪除器(unique_ptr模板參數(shù)指定,shared_ptr構(gòu)造函數(shù)傳入)
    unique_ptr<Date, DeleteArray<Date>> up2(new Date[5]);
    shared_ptr<Date> sp2(new Date[5], DeleteArray<Date>());
    // 函數(shù)指針作為刪除器
    unique_ptr<Date, void(*)(Date*)> up3(new Date[5], DeleteArrayFunc<Date>);
    shared_ptr<Date> sp3(new Date[5], DeleteArrayFunc<Date>);
    // lambda表達(dá)式作為刪除器
    auto delArrOBJ = [](Date* ptr) { delete[] ptr; };
    unique_ptr<Date, decltype(delArrOBJ)> up4(new Date[5], delArrOBJ);
    shared_ptr<Date> sp4(new Date[5], delArrOBJ);
    // 管理文件句柄
    shared_ptr<FILE> sp5(fopen("Test.cpp", "r"), Fclose());
    shared_ptr<FILE> sp6(fopen("Test.cpp", "r"), [](FILE* ptr) {
        cout << "fclose:" << ptr << endl;
        fclose(ptr);
    });
    return 0;
}

3.4 其他實(shí)用特性

make_shared構(gòu)造:直接通過參數(shù)初始化資源,更高效(減少一次內(nèi)存分配):

int main() {
    shared_ptr<Date> sp2 = make_shared<Date>(2024, 9, 11); // 直接初始化日期
    auto sp3 = make_shared<Date>(2024, 9, 11); // 自動(dòng)推導(dǎo)類型
    return 0;
}

operator bool類型轉(zhuǎn)換:直接判斷智能指針是否管理資源:

int main() {
    shared_ptr<Date> sp1(new Date(2024, 9, 11));
    shared_ptr<Date> sp4;
    if (sp1) // 等價(jià)于sp1.operator bool(),管理資源返回true
        cout << "sp1 is not nullptr" << endl;
    if (!sp4) // 未管理資源返回false
        cout << "sp4 is nullptr" << endl;
    return 0;
}

explicit構(gòu)造:禁止普通指針隱式轉(zhuǎn)換為智能指針(文末有介紹),避免意外錯(cuò)誤:

int main() {
    // 錯(cuò)誤:explicit構(gòu)造禁止隱式轉(zhuǎn)換
    // shared_ptr<Date> sp5 = new Date(2024, 9, 11);
    // unique_ptr<Date> sp6 = new Date(2024, 9, 11);
    return 0;
}

四、智能指針的核心原理

4.1auto_ptr原理(管理權(quán)轉(zhuǎn)移)

auto_ptr的核心是 “拷貝時(shí)轉(zhuǎn)移資源所有權(quán)”,導(dǎo)致原對(duì)象懸空,這是其被廢棄的根本原因

namespace zephyr {
    template<class T>
    class auto_ptr {
    public:
        auto_ptr(T* ptr) : _ptr(ptr) {}
        // 拷貝構(gòu)造:轉(zhuǎn)移所有權(quán)
        auto_ptr(auto_ptr<T>& sp) : _ptr(sp._ptr) {
            sp._ptr = nullptr; // 原對(duì)象懸空
        }
        // 賦值運(yùn)算符:轉(zhuǎn)移所有權(quán)
        auto_ptr<T>& operator=(auto_ptr<T>& ap) {
            if (this != &ap) { // 避免自賦值
                if (_ptr)
                    delete _ptr; // 釋放當(dāng)前資源
                _ptr = ap._ptr; // 轉(zhuǎn)移資源
                ap._ptr = nullptr; // 原對(duì)象懸空
            }
            return *this;
        }
        // 析構(gòu)釋放資源
        ~auto_ptr() {
            if (_ptr) {
                cout << "delete:" << _ptr << endl;
                delete _ptr;
            }
        }
        // 模擬指針行為
        T& operator*() { return *_ptr; }
        T* operator->() { return _ptr; }
    private:
        T* _ptr;
    };
}

4.2unique_ptr原理(禁止拷貝)

unique_ptr通過 “刪除拷貝構(gòu)造和賦值運(yùn)算符” 實(shí)現(xiàn)獨(dú)占所有權(quán),僅支持移動(dòng):

namespace zephyr {
    template<class T>
    class unique_ptr {
    public:
        explicit unique_ptr(T* ptr) : _ptr(ptr) {}
        // 移動(dòng)構(gòu)造:轉(zhuǎn)移資源所有權(quán)
        unique_ptr(unique_ptr<T>&& sp) : _ptr(sp._ptr) {
            sp._ptr = nullptr;
        }
        // 移動(dòng)賦值:轉(zhuǎn)移資源所有權(quán)
        unique_ptr<T>& operator=(unique_ptr<T>&& sp) {
            if (_ptr)
                delete _ptr;
            _ptr = sp._ptr;
            sp._ptr = nullptr;
            return *this;
        }
        // 析構(gòu)釋放資源
        ~unique_ptr() {
            if (_ptr) {
                cout << "delete:" << _ptr << endl;
                delete _ptr;
            }
        }
        // 模擬指針行為
        T& operator*() { return *_ptr; }
        T* operator->() { return _ptr; }
        // 禁止拷貝:刪除拷貝構(gòu)造和賦值運(yùn)算符
        unique_ptr(const unique_ptr<T>& sp) = delete;
        unique_ptr<T>& operator=(const unique_ptr<T>& sp) = delete;
    private:
        T* _ptr;
    };
}

4.3shared_ptr原理(引用計(jì)數(shù))

shared_ptr的核心是 “引用計(jì)數(shù)”,通過堆上的計(jì)數(shù)變量跟蹤資源持有者數(shù)量:

namespace zephyr {
    template<class T>
    class shared_ptr {
    public:
        // 構(gòu)造:初始化資源和引用計(jì)數(shù)(計(jì)數(shù)初始為1)
        explicit shared_ptr(T* ptr = nullptr)
            : _ptr(ptr)
            , _pcount(new int(1))
            , _del([](T* ptr) { delete ptr; }) {} // 默認(rèn)刪除器
        // 帶自定義刪除器的構(gòu)造
        template<class D>
        shared_ptr(T* ptr, D del)
            : _ptr(ptr)
            , _pcount(new int(1))
            , _del(del) {}
        // 拷貝構(gòu)造:共享資源,引用計(jì)數(shù)+1
        shared_ptr(const shared_ptr<T>& sp)
            : _ptr(sp._ptr)
            , _pcount(sp._pcount)
            , _del(sp._del) {
            ++(*_pcount);
        }
        // 賦值運(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;
                _pcount = sp._pcount;
                _del = sp._del;
                ++(*_pcount); // 新資源計(jì)數(shù)+1
            }
            return *this;
        }
        // 釋放資源邏輯
        void release() {
            if (--(*_pcount) == 0) { // 計(jì)數(shù)為0,釋放資源和計(jì)數(shù)變量
                _del(_ptr); // 調(diào)用刪除器
                delete _pcount;
                _ptr = nullptr;
                _pcount = nullptr;
            }
        }
        // 析構(gòu):調(diào)用release釋放資源
        ~shared_ptr() {
            release();
        }
        // 模擬指針行為
        T& operator*() { return *_ptr; }
        T* operator->() { return _ptr; }
        // 實(shí)用接口
        T* get() const { return _ptr; } // 獲取原生指針
        int use_count() const { return *_pcount; } // 獲取引用計(jì)數(shù)
    private:
        T* _ptr; // 管理的資源指針
        int* _pcount; // 引用計(jì)數(shù)(堆上分配,支持共享)
        function<void(T*)> _del; // 自定義刪除器
    };
}

4.4weak_ptr原理(輔助共享)

weak_ptr不管理資源,僅作為shared_ptr的輔助,不增加引用計(jì)數(shù),用于解決循環(huán)引用問題

namespace zephyr {
    template<class T>
    class weak_ptr {
    public:
        weak_ptr() : _ptr(nullptr) {}
        // 僅支持通過shared_ptr構(gòu)造/賦值,不增加引用計(jì)數(shù)
        weak_ptr(const shared_ptr<T>& sp) : _ptr(sp.get()) {}
        weak_ptr<T>& operator=(const shared_ptr<T>& sp) {
            _ptr = sp.get();
            return *this;
        }
    private:
        T* _ptr; // 僅存儲(chǔ)資源指針,不參與管理
    };
}

五、shared_ptr的核心問題與解決方案

5.1 循環(huán)引用問題

shared_ptr的共享特性可能導(dǎo)致循環(huán)引用,使引用計(jì)數(shù)無(wú)法歸零,最終造成內(nèi)存泄漏。

問題代碼(循環(huán)引用)

struct ListNode {
    int _data;
    shared_ptr<ListNode> _next; // 共享下一個(gè)節(jié)點(diǎn)
    shared_ptr<ListNode> _prev; // 共享上一個(gè)節(jié)點(diǎn)
    ~ListNode() {
        cout << "~ListNode()" << endl; // 循環(huán)引用時(shí)不會(huì)執(zhí)行
    }
};
int main() {
    shared_ptr<ListNode> n1(new ListNode);
    shared_ptr<ListNode> n2(new ListNode);
    cout << n1.use_count() << endl; // 輸出1
    cout << n2.use_count() << endl; // 輸出1
    n1->_next = n2; // n2的引用計(jì)數(shù)變?yōu)?
    n2->_prev = n1; // n1的引用計(jì)數(shù)變?yōu)?
    cout << n1.use_count() << endl; // 輸出2
    cout << n2.use_count() << endl; // 輸出2
    // 析構(gòu)n1和n2時(shí),引用計(jì)數(shù)均變?yōu)?,無(wú)法歸零,資源泄漏
    return 0;
}

問題分析

  • n1的析構(gòu)依賴n2->_prev的釋放。
  • n2的析構(gòu)依賴n1->_next的釋放。
  • 兩者形成循環(huán)依賴,引用計(jì)數(shù)無(wú)法歸零,資源永遠(yuǎn)不會(huì)釋放。

循環(huán)引用問題總結(jié)分析

1. 循環(huán)引用的 “本質(zhì)原因”:引用計(jì)數(shù)無(wú)法歸 0

循環(huán)引用導(dǎo)致內(nèi)存泄漏的核心是:當(dāng)所有外部 shared_ptr(如 main 中的 n1n2)的生命周期結(jié)束后,對(duì)象之間的內(nèi)部 shared_ptr 引用仍然互相維持,使得每個(gè)對(duì)象的引用計(jì)數(shù)都大于 0,從而無(wú)法觸發(fā)析構(gòu)和內(nèi)存釋放。

2. 循環(huán)引用的 “結(jié)構(gòu)表現(xiàn)”:不一定是嚴(yán)格的 “環(huán)形”,但必然存在 “互相引用的環(huán)”

所謂 “循環(huán)”,是這種 “引用計(jì)數(shù)無(wú)法歸 0” 的典型結(jié)構(gòu)表現(xiàn),但不是唯一形式。只要存在對(duì)象之間通過 shared_ptr 互相引用形成的 “環(huán)”,就會(huì)導(dǎo)致該問題。

解決方案:weak_ptr

ListNode_next_prev改為weak_ptr,不增加引用計(jì)數(shù),打破循環(huán):

struct ListNode {
    int _data;
    weak_ptr<ListNode> _next; // 改為weak_ptr
    weak_ptr<ListNode> _prev; // 改為weak_ptr
    ~ListNode() {
        cout << "~ListNode()" << endl; // 正常執(zhí)行
    }
};
int main() {
    shared_ptr<ListNode> n1(new ListNode);
    shared_ptr<ListNode> n2(new ListNode);
    n1->_next = n2; // weak_ptr不增加n2的引用計(jì)數(shù)(仍為1)
    n2->_prev = n1; // weak_ptr不增加n1的引用計(jì)數(shù)(仍為1)
    // 析構(gòu)n1和n2時(shí),引用計(jì)數(shù)均變?yōu)?,資源正常釋放
    return 0;
}

5.2weak_ptr的實(shí)用接口

weak_ptr不直接訪問資源(無(wú)operator*/operator->),需通過lock()獲取shared_ptr,確保資源有效:

int main() {
    shared_ptr<string> sp1(new string("111111"));
    shared_ptr<string> sp2(sp1);
    weak_ptr<string> wp = sp1;
    cout << wp.expired() << endl; // 輸出0:資源未過期
    cout << wp.use_count() << endl; // 輸出2:獲取引用計(jì)數(shù)
    // sp1和sp2轉(zhuǎn)移資源,原資源釋放
    sp1 = make_shared<string>("222222");
    sp2 = make_shared<string>("333333");
    cout << wp.expired() << endl; // 輸出1:資源已過期
    cout << wp.use_count() << endl; // 輸出0
    // 重新綁定資源
    wp = sp1;
    auto sp3 = wp.lock(); // 資源有效時(shí),返回非空shared_ptr
    if (sp3) {
        *sp3 += "###";
        cout << *sp1 << endl; // 輸出"222222###"
    }
    return 0;
}

六、shared_ptr的線程安全問題

6.1 問題本質(zhì)

  • 引用計(jì)數(shù)的線程安全shared_ptr的引用計(jì)數(shù)存儲(chǔ)在堆上,多線程拷貝 / 析構(gòu)時(shí)會(huì)并發(fā)修改計(jì)數(shù),導(dǎo)致數(shù)據(jù)競(jìng)爭(zhēng)。
  • 資源對(duì)象的線程安全shared_ptr指向的對(duì)象本身無(wú)線程安全保障,需用戶手動(dòng)控制(如加鎖)。

問題代碼(線程安全隱患)

struct AA {
    int _a1 = 0;
    int _a2 = 0;
    ~AA() {
        cout << "~AA()" << endl;
    }
};
int main() {
    zephyr::shared_ptr<AA> p(new AA);
    const size_t n = 100000;
    mutex mtx;
    // 多線程拷貝智能指針,修改資源對(duì)象
    auto func = [&]() {
        for (size_t i = 0; i < n; ++i) {
            zephyr::shared_ptr<AA> copy(p); // 拷貝時(shí)修改引用計(jì)數(shù)(線程不安全)
            unique_lock<mutex> lk(mtx); // 資源對(duì)象加鎖,保證線程安全
            copy->_a1++;
            copy->_a2++;
        }
    };
    thread t1(func);
    thread t2(func);
    t1.join();
    t2.join();
    cout << p->_a1 << endl; // 可能小于200000(計(jì)數(shù)競(jìng)爭(zhēng)導(dǎo)致部分操作失效)
    cout << p->_a2 << endl;
    cout << p.use_count() << endl;
    return 0;
}

6.2 解決方案

將引用計(jì)數(shù)改為原子類型(atomic<int>),保證計(jì)數(shù)修改的原子性:

namespace zephyr {
    template<class T>
    class shared_ptr {
    private:
        // 其他成員不變,僅修改引用計(jì)數(shù)類型
        atomic<int>* _pcount; // 原子類型,保證線程安全
    };
}

七、C++11 與 Boost 智能指針的關(guān)系

Boost 庫(kù)是 C++ 標(biāo)準(zhǔn)庫(kù)的重要參考,智能指針的發(fā)展歷程如下:

  1. C++98:推出首個(gè)智能指針auto_ptr,設(shè)計(jì)缺陷明顯。
  2. Boost 庫(kù):提供scoped_ptr(獨(dú)占)、shared_ptr(共享)、weak_ptr(輔助)、scoped_array(數(shù)組獨(dú)占)等,實(shí)用性更強(qiáng)。
  3. C++ TR1:引入shared_ptr,但非標(biāo)準(zhǔn)正式內(nèi)容。
  4. C++11:正式引入unique_ptr(對(duì)應(yīng) Boost 的scoped_ptr)、shared_ptr、weak_ptr,實(shí)現(xiàn)原理參考 Boost 庫(kù)。

八、內(nèi)存泄漏的全面解析

8.1 內(nèi)存泄漏的定義與危害

  • 定義:程序分配內(nèi)存后,因設(shè)計(jì)錯(cuò)誤失去對(duì)該內(nèi)存的控制,導(dǎo)致內(nèi)存無(wú)法回收。
  • 危害:短期運(yùn)行程序影響較小,長(zhǎng)期運(yùn)行程序(如服務(wù)器、操作系統(tǒng))會(huì)因可用內(nèi)存持續(xù)減少,導(dǎo)致響應(yīng)變慢甚至崩潰。

示例(無(wú)害的內(nèi)存泄漏)

int main() {
    // 分配1G內(nèi)存未釋放,但程序立即結(jié)束,進(jìn)程退出時(shí)系統(tǒng)回收資源
    char* ptr = new char[1024 * 1024 * 1024];
    cout << (void*)ptr << endl;
    return 0;
}

8.2 內(nèi)存泄漏的檢測(cè)工具

  • Linux:Valgrind、AddressSanitizer。
  • Windows:VLD(Visual Leak Detector)、BoundsChecker。

8.3 內(nèi)存泄漏的避免方法

  1. 遵循良好的編碼規(guī)范,手動(dòng)匹配new/delete(理想狀態(tài))。
  2. 優(yōu)先使用智能指針管理動(dòng)態(tài)資源,利用 RAII 思想自動(dòng)釋放。
  3. 自定義 RAII 類管理特殊資源(如文件句柄、網(wǎng)絡(luò)連接)。
  4. 項(xiàng)目上線前使用檢測(cè)工具排查泄漏。

總結(jié)

智能指針是 C++ 動(dòng)態(tài)內(nèi)存管理的核心解決方案,基于 RAII 思想實(shí)現(xiàn)資源的自動(dòng)釋放。unique_ptr適用于獨(dú)占資源場(chǎng)景,shared_ptr適用于共享資源場(chǎng)景,weak_ptr用于解決循環(huán)引用問題。掌握智能指針的原理、使用場(chǎng)景及核心問題(循環(huán)引用、線程安全),能有效避免內(nèi)存泄漏,提升代碼的健壯性和可維護(hù)性。

文章相關(guān)問題答疑及講解:

1.shared_ptr和unique_ptr都得構(gòu)造函數(shù)都使?explicit 修飾,防止普通指針隱式類型轉(zhuǎn)換成智能指針對(duì)象。

場(chǎng)景模擬:未用 explicit 修飾的智能指針構(gòu)造函數(shù)

假設(shè)我們自定義一個(gè)簡(jiǎn)化的智能指針(模擬未加 explicit 的情況):

template<class T>
class MySmartPtr {
public:
    // 未用 explicit 修飾的構(gòu)造函數(shù)(危險(xiǎn)!)
    MySmartPtr(T* ptr) : _ptr(ptr) {} 
    ~MySmartPtr() {
        delete _ptr; // 析構(gòu)時(shí)釋放內(nèi)存
        cout << "內(nèi)存已釋放" << endl;
    }
    T& operator*() { return *_ptr; }
private:
    T* _ptr;
};

普通指針的隱式轉(zhuǎn)換案例

定義一個(gè)接收智能指針作為參數(shù)的函數(shù),然后傳遞普通指針:

// 函數(shù)參數(shù)為智能指針類型
void UseResource(MySmartPtr<int> sp) {
    cout << "使用資源:" << *sp << endl;
}
int main() {
    int* raw_ptr = new int(100); // 普通指針
    // 隱式轉(zhuǎn)換:raw_ptr 被自動(dòng)轉(zhuǎn)換為 MySmartPtr<int> 對(duì)象
    UseResource(raw_ptr); 
    // 危險(xiǎn)!此時(shí) raw_ptr 指向的內(nèi)存已被 UseResource 中智能指針的析構(gòu)函數(shù)釋放
    cout << *raw_ptr << endl; // 訪問已釋放內(nèi)存(未定義行為,可能崩潰)
    delete raw_ptr; // 重復(fù)釋放(必然崩潰)
    return 0;
}

2.自定義刪除器的必要性(何時(shí)需要傳遞析構(gòu)方法)

只有當(dāng)需要管理非new分配的資源(如文件句柄、動(dòng)態(tài)庫(kù)指針、通過 malloc 分配的內(nèi)存等)時(shí),才需要顯式傳遞自定義刪除器。例如:

  • 釋放 malloc 分配的內(nèi)存需要用 free,而非 delete;
  • 關(guān)閉文件句柄需要用 fclose,而非內(nèi)存釋放操作。

我的代碼的模擬實(shí)現(xiàn)中,shared_ptr 的構(gòu)造函數(shù) explicit shared_ptr(T* ptr, X del) 正是為這種場(chǎng)景設(shè)計(jì)的 —— 允許用戶傳遞自定義的釋放邏輯(del),而默認(rèn)構(gòu)造函數(shù) explicit shared_ptr(T* ptr = nullptr) 則使用默認(rèn)刪除器 [](T* ptr) {delete ptr;},這與標(biāo)準(zhǔn)庫(kù)的設(shè)計(jì)思路完全一致。

標(biāo)準(zhǔn)庫(kù)中傳遞自定義刪除器的示例:

#include <memory>
#include <cstdio>
using namespace std;
int main() {
    // 管理 FILE* 資源,自定義刪除器用 fclose 釋放
    FILE* fp = fopen("test.txt", "w");
    shared_ptr<FILE> sp(fp, [](FILE* p) { 
        fclose(p); 
        cout << "文件已關(guān)閉" << endl; 
    }); 
    return 0;
}

3.為什么使用unique_ptr時(shí)函數(shù)指針 /lambda 也需要傳入實(shí)例?

1)unique_ptr的模板參數(shù):僅指定刪除器的 “類型”

unique_ptr的模板參數(shù)class Deleter(第二個(gè)參數(shù))的作用是聲明刪除器的類型,它告訴編譯器:“這個(gè)unique_ptr將使用某種類型的刪除器來(lái)釋放資源”。

例如:

unique_ptr<Date, void(*)(Date*)> up3;

這里的void(*)(Date*)是一個(gè)函數(shù)指針類型(指向 “接收Date*參數(shù)、返回void的函數(shù)” 的指針),它僅聲明了 “up3的刪除器必須是這種類型的函數(shù)指針”,但并沒有指定具體用哪個(gè)函數(shù)指針。

2) 構(gòu)造函數(shù)的參數(shù):必須提供刪除器的 “實(shí)例”

unique_ptr需要知道具體調(diào)用哪個(gè)刪除器來(lái)釋放資源。模板參數(shù)只規(guī)定了刪除器的類型,而具體的刪除器實(shí)例(即實(shí)際執(zhí)行刪除操作的函數(shù)指針)必須通過構(gòu)造函數(shù)傳入。

例如,DeleteArrayFunc<Date>是一個(gè)符合void(*)(Date*)類型的函數(shù)指針實(shí)例(它指向真正執(zhí)行delete[]的函數(shù))。如果不傳入這個(gè)實(shí)例:

// 錯(cuò)誤示例:只指定類型,未傳入具體刪除器實(shí)例
unique_ptr<Date, void(*)(Date*)> up3(new Date[5]); 

unique_ptr將無(wú)法知道 “到底用哪個(gè)函數(shù)指針來(lái)釋放new Date[5]分配的資源”,編譯時(shí)會(huì)報(bào)錯(cuò)(缺少刪除器實(shí)例)。

其實(shí)不僅是函數(shù)指針,仿函數(shù)和 lambda 作為刪除器時(shí),都應(yīng)傳入實(shí)例,仿函數(shù)沒有傳入實(shí)例是因?yàn)樵趗nique_ptr構(gòu)造時(shí)隱式構(gòu)造了一個(gè)仿函數(shù)實(shí)例:

// 仿函數(shù)刪除器:
unique_ptr<Date, DeleteArray<Date>> up2(new Date[5]); 
unique_ptr<Date, DeleteArray<Date>> up2(new Date[5], DeleteArray<Date>()); 
// (省略時(shí)其實(shí)是隱式構(gòu)造了一個(gè)實(shí)例,等價(jià)于上面的寫法)
// lambda刪除器:傳入了delArrOBJ這個(gè)實(shí)例
unique_ptr<Date, decltype(delArrOBJ)> up4(new Date[5], delArrOBJ);
//lambda表達(dá)式的特殊之處在于無(wú)法用推導(dǎo)出的類型來(lái)隱式構(gòu)造一個(gè)實(shí)例
//lambda 閉包類型的構(gòu)造函數(shù)是 “受限的”,因?yàn)殚]包類型沒有默認(rèn)構(gòu)造函數(shù),且用戶無(wú)法手動(dòng)調(diào)用其構(gòu)造函數(shù)

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

相關(guān)文章

  • C++中的string類型

    C++中的string類型

    這篇文章主要介紹了C++中的string類型,在C++當(dāng)中,除了char 類型,還有專門的字符串類型,就叫做string,下面文字將圍繞其相關(guān)資料展開詳細(xì)內(nèi)容,需要的朋友可以參考一下,希望對(duì)你有所幫助
    2021-11-11
  • C++ OpenCV實(shí)現(xiàn)圖像雙三次插值算法詳解

    C++ OpenCV實(shí)現(xiàn)圖像雙三次插值算法詳解

    圖像雙三次插值的原理,就是目標(biāo)圖像的每一個(gè)像素都是由原圖上相對(duì)應(yīng)點(diǎn)周圍的4x4=16個(gè)像素經(jīng)過加權(quán)之后再相加得到的。本文主要介紹了通過C++ OpenCV實(shí)現(xiàn)圖像雙三次插值算法,需要的可以參考一下
    2021-12-12
  • C語(yǔ)言中設(shè)置進(jìn)程優(yōu)先順序的方法

    C語(yǔ)言中設(shè)置進(jìn)程優(yōu)先順序的方法

    這篇文章主要介紹了C語(yǔ)言中設(shè)置進(jìn)程優(yōu)先順序的方法,包括setpriority()函數(shù)和getpriority()函數(shù)以及nice()函數(shù),需要的朋友可以參考下
    2015-08-08
  • C++ 讀寫文件安全又簡(jiǎn)潔的簡(jiǎn)單實(shí)例

    C++ 讀寫文件安全又簡(jiǎn)潔的簡(jiǎn)單實(shí)例

    這篇文章主要介紹了C++ 讀寫文件安全又簡(jiǎn)潔的簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • 關(guān)于C語(yǔ)言操作符的那些事(超級(jí)全)

    關(guān)于C語(yǔ)言操作符的那些事(超級(jí)全)

    這篇文章主要給大家介紹了關(guān)于C語(yǔ)言操作符的那些事兒,c語(yǔ)言的操作符有很多,包括算術(shù)操作符、移位操作符、位操作符、賦值操作符、單目操作符、關(guān)系操作符、邏輯操作符、條件操作符、逗號(hào)表達(dá)式、下標(biāo)引用、函數(shù)調(diào)用和結(jié)構(gòu)成員,需要的朋友可以參考下
    2021-08-08
  • 如何解決C語(yǔ)言,函數(shù)名與宏沖突

    如何解決C語(yǔ)言,函數(shù)名與宏沖突

    本文介紹了“如何解決C語(yǔ)言,函數(shù)名與宏沖突”,需要的朋友可以參考一下
    2013-03-03
  • C++11中異常處理機(jī)制詳解

    C++11中異常處理機(jī)制詳解

    傳統(tǒng)的C語(yǔ)言處理異常的方式有兩種:終止程序和返回錯(cuò)誤碼。在實(shí)際中的C語(yǔ)言程序基本都是通過返回錯(cuò)誤碼的方式來(lái)處理錯(cuò)誤的,部分情況下使用終止程序來(lái)處理比較嚴(yán)重的錯(cuò)誤。本文將通過示例和大家聊聊C++11中異常處理機(jī)制,需要的可以參考一下
    2022-09-09
  • qt實(shí)現(xiàn)倒計(jì)時(shí)示例

    qt實(shí)現(xiàn)倒計(jì)時(shí)示例

    這篇文章主要介紹了qt實(shí)現(xiàn)倒計(jì)時(shí)示例,需要的朋友可以參考下
    2014-05-05
  • C++詳解哈夫曼樹的概念與實(shí)現(xiàn)步驟

    C++詳解哈夫曼樹的概念與實(shí)現(xiàn)步驟

    給定N個(gè)權(quán)值作為N個(gè)葉子結(jié)點(diǎn),構(gòu)造一棵二叉樹,若該樹的帶權(quán)路徑長(zhǎng)度達(dá)到最小,稱這樣的二叉樹為最優(yōu)二叉樹,也稱為哈夫曼樹(Huffman?Tree)。哈夫曼樹是帶權(quán)路徑長(zhǎng)度最短的樹,權(quán)值較大的結(jié)點(diǎn)離根較近
    2022-04-04
  • Matlab實(shí)現(xiàn)簡(jiǎn)易紀(jì)念碑谷游戲的示例代碼

    Matlab實(shí)現(xiàn)簡(jiǎn)易紀(jì)念碑谷游戲的示例代碼

    《紀(jì)念碑谷》是USTWO公司開發(fā)制作的解謎類手機(jī)游戲,在游戲中,通過探索隱藏小路、發(fā)現(xiàn)視力錯(cuò)覺以及躲避神秘的烏鴉人來(lái)幫助沉默公主艾達(dá)走出紀(jì)念碑迷陣。本文將用Matlab編寫簡(jiǎn)易版的紀(jì)念碑谷游戲,感興趣的可以了解一下
    2022-03-03

最新評(píng)論

孟津县| 美姑县| 子洲县| 久治县| 青铜峡市| 奉新县| 唐海县| 清远市| 辉南县| 齐河县| 桂东县| 石楼县| 莫力| 台北县| 西丰县| 文山县| 渝中区| 阜平县| 天等县| 大余县| 金堂县| 巴彦淖尔市| 台南市| 轮台县| 象州县| 阳谷县| 吴堡县| 宁蒗| 南漳县| 包头市| 清水河县| 乌恰县| 同仁县| 安阳市| 延边| 轮台县| 吴旗县| 黑河市| 牙克石市| 临沭县| 土默特左旗|