首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 智慧指標全家族:auto_ptr、scoped_ptr、shared_ptr、weak_ptr 演進與實戰

     編輯:關於C++

為何需要智慧指標?

裸指標 new/delete 易導致記憶體洩漏 (異常跳過 delete)、二次釋放、懸空指標。智慧指標以RAII (Resource Acquisition Is Initialization) 封裝指標,建構時獲取資源,解構時自動釋放,保證異常安全。

四代智慧指標演進

1. auto_ptr (C++98,C++11 棄用,C++17 移除)

擁有權轉移 語意:拷貝/指派會將來源指標置空,目的地接管擁有權。

auto_ptr p1(new Foo);
auto_ptr p2 = p1;  // p1 變空,p2 擁有
// 缺點:不可放入 STL 容器、不可共享擁有權、const 不正確

2. scoped_ptr (Boost / 非標準)

獨占擁有權,不可複製、不可移動 (私有拷貝建構/指派)。適合單一作用域內的獨佔資源。

scoped_ptr p(new Foo);  // 無拷貝、無指派
// 現代替代:std::unique_ptr

3. unique_ptr (C++11 標準)

獨占擁有權,可移動、不可複製。零開銷抽象,支援陣列 unique_ptr、自定義刪除器、轉換 shared_ptr。

unique_ptr p1 = make_unique();
unique_ptr p2 = move(p1);  // 移動轉移擁有權
// 首選獨占資源管理工具

4. shared_ptr (C++11 標準)

共享擁有權,引用計數。控制區塊儲存強引用計數、弱引用計數、刪除器、配置器。make_shared 單次配置物件+控制區塊。

auto p1 = make_shared();
auto p2 = p1;  // 計數+1
// 計數歸零時 delete

5. weak_ptr (C++11 標準)

非擁有權觀察者,不增加強引用計數。必須 lock() 取得 shared_ptr 才能存取。解決 shared_ptr 循環引用 (雙向鏈表、父子雙向、觀察者模式、快取)。

struct Node { shared_ptr next; weak_ptr prev; };
// prev 不增加計數,打破循環

shared_ptr 循環引用實戰

struct Node {
    int data;
    shared_ptr next;
    weak_ptr prev;  // 弱引用打破循環
};

void test() {
    auto a = make_shared();
    auto b = make_shared();
    a->next = b;
    b->prev = a;  // weak_ptr,計數維持 1
}  // 正常釋放

shared_ptr 循環引用:雙向指標導致計數卡住

自定義刪除器模式

管理非 new 資源:

// FILE*
shared_ptr fp(fopen("f.txt","w"), fclose);
// malloc
shared_ptr buf(malloc(100), free);
// C 陣列
shared_ptr arr(new int[10], [](int* p){ delete[] p; });
// Windows HANDLE
shared_ptr h(CreateFile(...), [](void* h){ CloseHandle(h); });

模擬實作 shared_ptr 核心邏輯 (教學用)

template
class SharedPtr {
    T* ptr;
    int* count;  // 強引用計數
public:
    SharedPtr(T* p = nullptr) : ptr(p), count(p ? new int(1) : nullptr) {}
    ~SharedPtr() { dec(); }

    SharedPtr(const SharedPtr& rhs) : ptr(rhs.ptr), count(rhs.count) { inc(); }
    SharedPtr& operator=(const SharedPtr& rhs) {
        if (this != &rhs) { dec(); ptr = rhs.ptr; count = rhs.count; inc(); }
        return *this;
    }

    T& operator*() const { return *ptr; }
    T* operator->() const { return ptr; }
    int use_count() const { return count ? *count : 0; }

private:
    void inc() { if (count) ++*count; }
    void dec() { if (count && --*count == 0) { delete ptr; delete count; } }
};

選型決策樹

graph TD A[需要共享擁有權?] -->|否| B[獨占擁有權] A -->|是| C[shared_ptr + weak_ptr 打破循環] B --> D[需要移動語意?] D -->|是| E[unique_ptr] D -->|否(舊碼)| F[scoped_ptr / auto_ptr]

最佳實踐清單

  1. 首選 make_unique / make_shared:例外安全、效能優化。
  2. 獨占資源用 unique_ptr,參數傳遞用 unique_ptr& 或 T* (觀察者)。
  3. 共享資源用 shared_ptr,觀察者/打破循環用 weak_ptr。
  4. 避免 shared_ptr 管理 this (需 enable_shared_from_this)。
  5. 自定義刪除器型別是 shared_ptr 型別一部分,勿混用。
  6. 陣列用 unique_ptr 或 shared_ptr(new T[n], [](T* p){delete[] p;})。
  7. 循環引用場景:雙向鏈表、樹狀結構父指標、觀察者模式、快取 → 必用 weak_ptr。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved