名稱空間
變體
操作

std::shared_ptr

來自 cppreference.com
< cpp‎ | 記憶體
 
 
記憶體管理庫
(僅作說明*)
未初始化記憶體演算法
(C++17)
(C++17)
(C++17)
受約束的未初始化
記憶體演算法
C 庫

分配器
記憶體資源
垃圾回收支援
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
(C++11)(直到 C++23)
未初始化儲存
(直到 C++20*)
(直到 C++20*)
顯式生命週期管理
 
 
定義於標頭檔案 <memory>
template< class T > class shared_ptr;
(C++11 起)

std::shared_ptr 是一個智慧指標,它透過指標共享物件的所有權。多個 shared_ptr 物件可以擁有同一個物件。當以下任一情況發生時,物件會被銷燬,其記憶體會被釋放:

  • 最後一個擁有該物件的 shared_ptr 被銷燬;
  • 最後一個擁有該物件的 shared_ptr 透過 operator=reset() 被賦予另一個指標。

物件透過 delete-expression 或在構造 shared_ptr 時提供的自定義刪除器進行銷燬。

一個 shared_ptr 可以共享一個物件的所有權,同時儲存指向另一個物件的指標。此功能可用於指向成員物件,同時擁有它們所屬的物件。儲存的指標是 get()、解引用和比較運算子所訪問的指標。當引用計數為零時,傳遞給刪除器的指標是受管理指標。

一個 shared_ptr 也可以不擁有任何物件,在這種情況下它被稱為 *空* (如果使用別名建構函式建立空 shared_ptr,它可能有一個非空的儲存指標)。

shared_ptr 的所有特化都滿足 CopyConstructibleCopyAssignableLessThanComparable 的要求,並且可以 上下文轉換為 bool

所有成員函式(包括複製建構函式和複製賦值)可以在不同 shared_ptr 物件上被多個執行緒呼叫,而無需額外同步,即使這些物件是副本並共享同一物件的所有權。如果多個執行執行緒在沒有同步的情況下訪問同一個 shared_ptr 物件,並且其中任何一個訪問使用了 shared_ptr 的非 const 成員函式,則會發生資料競爭;可以使用 std::atomic 來防止資料競爭。

目錄

[編輯] 成員型別

成員型別 定義
element_type
T (C++17 前)
std::remove_extent_t<T> (C++17 起)
weak_type (從 C++17 開始) std::weak_ptr<T>

[編輯] 成員函式

構造新的 shared_ptr
(公共成員函式) [編輯]
如果沒有更多的 shared_ptr 連結到它,則銷燬所擁有的物件。
(公共成員函式) [編輯]
賦值 shared_ptr
(公共成員函式) [編輯]
修改器
替換託管物件
(公共成員函式) [編輯]
交換管理物件
(公共成員函式) [編輯]
觀察器
返回儲存的指標
(公共成員函式) [編輯]
解引用儲存的指標
(公共成員函式) [編輯]
提供對儲存陣列的索引訪問
(公共成員函式) [編輯]
返回引用同一管理物件的 shared_ptr 物件的數量
(公共成員函式) [編輯]
(C++20 前)
檢查管理物件是否僅由當前的 shared_ptr 物件管理
(公共成員函式) [編輯]
檢查儲存的指標是否不為空
(公共成員函式) [編輯]
提供共享指標的基於所有權的排序
(公共成員函式) [編輯]
提供基於所有者的共享指標雜湊
(公共成員函式) [編輯]
提供基於所有者的共享指標相等比較
(公共成員函式) [編輯]

[編輯] 非成員函式

建立一個管理新物件的共享指標
(函式模板) [編輯]
建立一個共享指標,該指標管理使用分配器分配的新物件
(函式模板) [編輯]
對儲存的指標應用 static_castdynamic_castconst_castreinterpret_cast
(函式模板) [編輯]
如果擁有,則返回指定型別的刪除器
(函式模板) [編輯]
(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(C++20)
與另一個 shared_ptrnullptr 比較
(函式模板) [編輯]
將儲存的指標值輸出到輸出流
(函式模板) [編輯]
特化 std::swap 演算法
(函式模板) [編輯]
特化 std::shared_ptr 的原子操作
(函式模板) [編輯]

[編輯] 輔助類

原子共享指標
(類模板特化) [編輯]
std::shared_ptr 的雜湊支援
(類模板特化) [編輯]

[編輯] 推導指南 (從 C++17 開始)

[編輯] 注意事項

一個物件的所有權只能透過複製構造或複製賦值其值給另一個 shared_ptr 來與另一個 shared_ptr 共享。使用由另一個 shared_ptr 擁有的原始底層指標構造新的 shared_ptr 會導致未定義行為。

std::shared_ptr 可以與 不完整型別 T 一起使用。但是,來自原始指標的建構函式(template<class Y> shared_ptr(Y*))和 template<class Y> void reset(Y*) 成員函式只能使用指向完整型別的指標呼叫(請注意,std::unique_ptr 可以由指向不完整型別的原始指標構造)。

std::shared_ptr<T> 中的 T 可以是函式型別:在這種情況下,它管理函式指標,而不是物件指標。這有時用於在任何函式被引用時保持動態庫或外掛載入。

void del(void(*)()) {}
 
void fun() {}
 
int main()
{
    std::shared_ptr<void()> ee(fun, del);
    (*ee)();
}

[編輯] 實現說明

在典型的實現中,shared_ptr 只持有兩個指標

  • 儲存的指標(由 get() 返回);
  • 指向 *控制塊* 的指標。

控制塊是一個動態分配的物件,它包含

  • 指向管理物件的指標或管理物件本身;
  • 刪除器(型別擦除);
  • 分配器(型別擦除);
  • 擁有管理物件的 shared_ptr 數量;
  • 引用管理物件的 weak_ptr 數量。

當透過呼叫 std::make_sharedstd::allocate_shared 建立 shared_ptr 時,控制塊和管理物件的記憶體都是透過單個分配建立的。管理物件在控制塊的資料成員中就地構造。當透過 shared_ptr 的一個建構函式建立 shared_ptr 時,管理物件和控制塊必須單獨分配。在這種情況下,控制塊儲存指向管理物件的指標。

shared_ptr 直接持有的指標是 get() 返回的指標,而由控制塊持有的指標/物件是當共享所有者數量達到零時將被刪除的指標/物件。這些指標不一定相等。

shared_ptr 的解構函式遞減控制塊的共享所有者數量。如果該計數器達到零,控制塊會呼叫管理物件的解構函式。控制塊本身不會被釋放,直到 std::weak_ptr 計數器也達到零。

在現有實現中,如果存在指向同一控制塊的共享指標,則弱指標的數量會增加([1][2])。

為了滿足執行緒安全要求,引用計數器通常使用相當於 std::atomic::fetch_addstd::memory_order_relaxed 的操作來遞增(遞減需要更強的排序以安全地銷燬控制塊)。

[編輯] 示例

#include <chrono>
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
 
using namespace std::chrono_literals;
 
struct Base
{
    Base() { std::cout << "Base::Base()\n"; }
 
    // Note: non-virtual destructor is OK here
    ~Base() { std::cout << "Base::~Base()\n"; }
};
 
struct Derived : public Base
{
    Derived() { std::cout << "Derived::Derived()\n"; }
 
    ~Derived() { std::cout << "Derived::~Derived()\n"; }
};
 
void print(auto rem, std::shared_ptr<Base> const& sp)
{
    std::cout << rem << "\n\tget() = " << sp.get()
              << ", use_count() = " << sp.use_count() << '\n';
}
 
void thr(std::shared_ptr<Base> p)
{
    std::this_thread::sleep_for(987ms);
    std::shared_ptr<Base> lp = p; // thread-safe, even though the
                                  // shared use_count is incremented
    {
        static std::mutex io_mutex;
        std::lock_guard<std::mutex> lk(io_mutex);
        print("Local pointer in a thread:", lp);
    }
}
 
int main()
{
    std::shared_ptr<Base> p = std::make_shared<Derived>();
 
    print("Created a shared Derived (as a pointer to Base)", p);
 
    std::thread t1{thr, p}, t2{thr, p}, t3{thr, p};
    p.reset(); // release ownership from main
 
    print("Shared ownership between 3 threads and released ownership from main:", p);
 
    t1.join();
    t2.join();
    t3.join();
 
    std::cout << "All threads completed, the last one deleted Derived.\n";
}

可能的輸出

Base::Base()
Derived::Derived()
Created a shared Derived (as a pointer to Base)
	get() = 0x118ac30, use_count() = 1
Shared ownership between 3 threads and released ownership from main:
	get() = 0, use_count() = 0
Local pointer in a thread:
	get() = 0x118ac30, use_count() = 5
Local pointer in a thread:
	get() = 0x118ac30, use_count() = 4
Local pointer in a thread:
	get() = 0x118ac30, use_count() = 2
Derived::~Derived()
Base::~Base()
All threads completed, the last one deleted Derived.

[編輯] 示例

#include <iostream>
#include <memory>
 
struct MyObj
{
    MyObj() { std::cout << "MyObj constructed\n"; }
 
    ~MyObj() { std::cout << "MyObj destructed\n"; }
};
 
struct Container : std::enable_shared_from_this<Container> // note: public inheritance
{
    std::shared_ptr<MyObj> memberObj;
 
    void CreateMember() { memberObj = std::make_shared<MyObj>(); }
 
    std::shared_ptr<MyObj> GetAsMyObj()
    {
        // Use an alias shared ptr for member
        return std::shared_ptr<MyObj>(shared_from_this(), memberObj.get());
    }
};
 
#define COUT(str) std::cout << '\n' << str << '\n'
 
#define DEMO(...) std::cout << #__VA_ARGS__ << " = " << __VA_ARGS__ << '\n'
 
int main()
{
    COUT("Creating shared container");
    std::shared_ptr<Container> cont = std::make_shared<Container>();
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
 
    COUT("Creating member");
    cont->CreateMember();
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
 
    COUT("Creating another shared container");
    std::shared_ptr<Container> cont2 = cont;
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    DEMO(cont2.use_count());
    DEMO(cont2->memberObj.use_count());
 
    COUT("GetAsMyObj");
    std::shared_ptr<MyObj> myobj1 = cont->GetAsMyObj();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    DEMO(cont2.use_count());
    DEMO(cont2->memberObj.use_count());
 
    COUT("Copying alias obj");
    std::shared_ptr<MyObj> myobj2 = myobj1;
    DEMO(myobj1.use_count());
    DEMO(myobj2.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
    DEMO(cont2.use_count());
    DEMO(cont2->memberObj.use_count());
 
    COUT("Resetting cont2");
    cont2.reset();
    DEMO(myobj1.use_count());
    DEMO(myobj2.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
 
    COUT("Resetting myobj2");
    myobj2.reset();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
    DEMO(cont->memberObj.use_count());
 
    COUT("Resetting cont");
    cont.reset();
    DEMO(myobj1.use_count());
    DEMO(cont.use_count());
}

輸出

Creating shared container
cont.use_count() = 1
cont->memberObj.use_count() = 0
 
Creating member
MyObj constructed
cont.use_count() = 1
cont->memberObj.use_count() = 1
 
Creating another shared container
cont.use_count() = 2
cont->memberObj.use_count() = 1
cont2.use_count() = 2
cont2->memberObj.use_count() = 1
 
GetAsMyObj
myobj1.use_count() = 3
cont.use_count() = 3
cont->memberObj.use_count() = 1
cont2.use_count() = 3
cont2->memberObj.use_count() = 1
 
Copying alias obj
myobj1.use_count() = 4
myobj2.use_count() = 4
cont.use_count() = 4
cont->memberObj.use_count() = 1
cont2.use_count() = 4
cont2->memberObj.use_count() = 1
 
Resetting cont2
myobj1.use_count() = 3
myobj2.use_count() = 3
cont.use_count() = 3
cont->memberObj.use_count() = 1
 
Resetting myobj2
myobj1.use_count() = 2
cont.use_count() = 2
cont->memberObj.use_count() = 1
 
Resetting cont
myobj1.use_count() = 1
cont.use_count() = 0
MyObj destructed

[編輯] 另請參閱

具有唯一物件所有權語義的智慧指標
(類模板) [編輯]
(C++11)
std::shared_ptr 管理的物件的弱引用
(類模板) [編輯]