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

C++11 unique_ptr 完全指南:獨占所有權、移動語義、自定義刪除器與容器應用

     編輯:關於C++

std::unique_ptr 是 C++11 引入的獨占式智能指針,取代已棄用的 auto_ptr。它嚴格遵循單一所有權原則:同一時刻只能有一個 unique_ptr 管理某對象,禁止拷貝,僅支持移動語義轉移所有權。零開銷、無控制塊、適合獨占資源場景。

核心特性對比

特性unique_ptrshared_ptr
所有權獨占共享
拷貝構造/賦值刪除(編譯錯誤)允許
移動構造/賦值允許(轉移所有權)允許
控制塊無有(引用計數、弱計數、刪除器、分配器)
數組支持原生 unique_ptr<T[]>需自定義刪除器
線程安全無共享狀態,天然安全控制塊原子操作
開銷極小(一個指針大小)較大(控制塊、原子操作)

關鍵操作

  • 構造:unique_ptr<T> p(new T)、make_unique<T>(args...) (C++14)
  • 釋放所有權:release() 返回裸指針並置空,調用者負責刪除
  • 重置:reset(new T) 刷舊換新,reset() 釋放
  • 獲取裸指針:get()(不轉移所有權)
  • 移動轉移:std::move(p1) → p2,p1 置空
  • 布爾轉換:可直接用於 if (p)
  • 數組版本:unique_ptr<int[]> arr(new int[5]),支持 arr[i]

自定義刪除器

unique_ptr 通過模板參數指定刪除器類型,刪除器作為成員存儲(空基優化):

// 函數指針刪除器
void my_deleter(int* p) { delete p; }
std::unique_ptr<int, void(*)(int*)> p1(new int, my_deleter);

// Lambda 刪除器 (需捕獲無狀態)
auto del = [](int* p) { delete p; };
std::unique_ptr<int, decltype(del)> p2(new int, del);

// 結構體刪除器
struct Deleter { void operator()(int* p) { delete p; } };
std::unique_ptr<int, Deleter> p3(new int);

實戰代碼示例

1. 基礎所有權轉移

struct Foo {
    Foo() { std::cout << "Foo::Foo\n"; }
    ~Foo() { std::cout << "Foo::~Foo\n"; }
    void bar() { std::cout << "Foo::bar\n"; }
};

int test_unique_ptr1() {
    std::unique_ptr<Foo> p1(new Foo);
    if (p1) p1->bar();
    
    { // 內部作用域
        std::unique_ptr<Foo> p2(std::move(p1)); // p2 接管
        p1 = std::move(p2); // 權限回歸 p1
        std::cout << "destroying p2...\n";
    } // p2 銷毀,但對象仍存活(p1 持有)
    
    if (p1) p1->bar(); // 繼續使用
    return 0; // p1 銷毀,對象釋放
}

2. 各種構造方式

int test_unique_ptr2() {
    std::default_delete<int> d;
    std::unique_ptr<int> u1;                    // 空
    std::unique_ptr<int> u2(nullptr);           // 空
    std::unique_ptr<int> u3(new int);           // 默認刪除器
    std::unique_ptr<int> u4(new int, d);        // 自定義刪除器
    std::unique_ptr<int> u5(new int, std::default_delete<int>());
    std::unique_ptr<int> u6(std::move(u5));     // 移動構造
    std::unique_ptr<int> u7(std::move(u6));
    std::unique_ptr<int> u8(std::auto_ptr<int>(new int)); // 從 auto_ptr 轉移 (C++11)
    
    // 狀態檢查
    auto print = [](const char* name, const std::unique_ptr<int>& p) {
        std::cout << name << ": " << (p ? "not null" : "null") << '\n';
    };
    print("u1", u1); print("u2", u2); print("u3", u3);
    print("u4", u4); print("u5", u5); print("u6", u6);
    print("u7", u7); print("u8", u8);
    return 0;
}

3. 容器與算法支持

int test_unique_ptr4() {
    // 函數參數傳遞(移動)
    void sink(std::unique_ptr<Foo> p) { ... }
    std::unique_ptr<Foo> source() { return std::make_unique<Foo>(); }
    
    auto pmain = source(); // C++17 RVO 或移動
    sink(std::move(pmain)); // 必須移動
    
    // 容器存儲
    std::vector<std::unique_ptr<int>> vi;
    vi.push_back(std::make_unique<int>(0));
    vi.push_back(std::make_unique<int>(3));
    vi.push_back(std::make_unique<int>(2));
    std::sort(vi.begin(), vi.end(), 
        [](const auto& a, const auto& b) { return *a < *b; });
    // 結果:{0, 2, 3}
    
    // 數組支持
    std::unique_ptr<int[]> arr(new int[5]);
    arr[0] = 5; // operator[] 可用
    // *arr 編譯錯誤:數組版本無 operator*
    return 0;
}

4. 自定義刪除器管理非內存資源

// 管理 FILE*
struct FileDeleter {
    void operator()(FILE* f) { if (f) fclose(f); }
};
std::unique_ptr<FILE, FileDeleter> fp(fopen("test.txt", "w"));

// 管理 Windows HANDLE
struct HandleDeleter {
    void operator()(HANDLE h) { if (h) CloseHandle(h); }
};
std::unique_ptr<void, HandleDeleter> h(CreateFile(...));

最佳實踐總結

  1. 優先使用 unique_ptr:單一所有權場景,零開銷,意圖明確
  2. 工廠函數返回 unique_ptr:std::make_unique<T>(args) (C++14) 或手工構造
  3. 需要共享時才用 shared_ptr:避免過早共享
  4. 避免裸 new/delete:RAII 管理所有資源
  5. 數組用 unique_ptr<T[]>:自動調用 delete[]
  6. 自定義刪除器管理非內存資源:FILE*、HANDLE、Socket、OpenGL 對象等
  7. 容器存儲 unique_ptr:配合移動語義、算法完美工作

unique_ptr 支持的操作表

GitHub 完整代碼:https://github.com/fengbingchun/Messy_Test

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved