std::make_shared, std::make_shared_for_overwrite

出自cppreference.com
 
 
內存管理庫
(僅用於闡述*)
分配器
未初始化內存算法
受約束的未初始化內存算法
內存資源
未初始化存儲 (C++20 前)
(C++17 棄用)
(C++17 棄用)

垃圾收集器支持 (C++23 前)
(C++11)(C++23 前)
(C++11)(C++23 前)
(C++11)(C++23 前)
(C++11)(C++23 前)
(C++11)(C++23 前)
(C++11)(C++23 前)
 
 
在標頭 <memory> 定義
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
(1) (C++11 起)
template< class T >
shared_ptr<T> make_shared( std::size_t N );
(2) (C++20 起)
template< class T >
shared_ptr<T> make_shared();
(3) (C++20 起)
template< class T >
shared_ptr<T> make_shared( std::size_t N, const std::remove_extent_t<T>& u );
(4) (C++20 起)
template< class T >
shared_ptr<T> make_shared( const std::remove_extent_t<T>& u );
(5) (C++20 起)
template< class T >
shared_ptr<T> make_shared_for_overwrite();
(6) (C++20 起)
template< class T >
shared_ptr<T> make_shared_for_overwrite( std::size_t N );
(7) (C++20 起)

為某個對象分配內存並以提供的實參初始化該對象。返回管理新創建的對象的 std::shared_ptr 對象。

1) 該對象具有 T 類型,並如同以 ::new (pv) T(std::forward<Args>(args)...) 構造,其中 pv 是指向適合持有 T 類型對象的存儲的 void* 指針。如果該對象要被銷毀,那麼它會如同以 pt->~T() 被銷毀,其中 pt 是指向該 T 類型對象的指針。

此重載只有在 T 不是數組類型時才會參與重載決議。

(C++20 起)
2) 該對象具有 std::remove_extent_t<T>[N] 類型。每個元素都具有默認的初始值。
此重載只有在 T 是無邊界的數組類型時才會參與重載決議。
3) 該對象具有 T 類型。每個元素都具有默認的初始值。
此重載只有在 T 是有邊界的數組類型時才會參與重載決議。
4) 該對象具有 std::remove_extent_t<T>[N] 類型。每個元素都具有初始值 u
此重載只有在 T 是無邊界的數組類型時才會參與重載決議。
5) 該對象具有 T 類型。每個元素都具有初始值 u
此重載只有在 T 是有邊界的數組類型時才會參與重載決議。
6) 該對象具有 T 類型。
  • 如果 T 不是數組類型,那麼如同以 ::new (pv) T 構造該對象,其中 pv 是指向適合持有 T 類型對象的存儲的 void* 指針。如果該對象要被銷毀,那麼它會如同以 pt->~T() 被銷毀,其中 pt 是指向該 T 類型對象的指針。
  • 如果 T 是有邊界的數組類型,那麼不指定每個元素的初始值。
此重載只有在 T 不是數組類型,或者是有邊界的數組類型時才會參與重載決議。
7) 該對象具有 std::remove_extent_t<T>[N] 類型。不指定每個元素的初始值。
此重載只有在 T 是無邊界的數組類型時才會參與重載決議。

初始化和銷毀數組元素

U 類型的數組元素會以它們地址的升序進行初始化。

  • 如果 U 不是數組類型,那麼每個元素如同以以下表達式構造,其中 pv 是指向適合持有 U 類型對象的存儲的 void* 指針:
2,3) ::new (pv) U()
4,5) ::new (pv) U(u)
6,7) ::new (pv) U
  • 否則遞歸地初始化每個元素各自的元素。對於數組的下一維度:
  • U 變成 std::remove_extent_t<U>
  • 對於重載 (4,5)u 變成它的對應元素。

當返回的 std::shared_ptr 管理的對象的生存期結束,或初始化數組元素時拋出異常,那麼已初始化的元素會按構造時的逆序銷毀。

對於每一個要被銷毀的具有非數組類型 U 的數組元素,它如同以 pu->~U() 被銷毀,其中 pu 是指向該 U 類型數組元素的指針。

(C++20 起)

參數

args - 將用以構造 T 對象的實參列表
N - 要使用的數組大小
u - 用以初始化數組每個元素的初值

返回值

指向具有 T 類型或在 T 是無邊界的數組類型時具有 std::remove_extent_t<T>[N] 類型(C++20 起)的對象的 std::shared_ptr

對於返回的 std::shared_ptr rr.get() 會返回非空指針,且 r.use_count() 會返回 1

異常

可能拋出 std::bad_alloc 或任何 T 構造函數所拋的異常。如果拋出異常,那麼函數無效果。如果異常在數組的構造中拋出,那麼已初始化元素以逆序銷毀。(C++20 起)

註解

這些函數通常會分配多於 sizeof(T) 的內存以儲存內部記錄結構,例如引用計數。

此函數可用作 std::shared_ptr<T>(new T(args...)) 的替代品。得失是:

  • std::shared_ptr<T>(new T(args...)) 進行至少二次分配(一次為 T 而另一次為共享指針的控制塊),而 std::make_shared<T> 典型地僅進行一次分配(標準推薦但不要求如此,所有已知實現均如此)。
  • 如果在所有共享擁有者的生存期結束後仍有任何 std::weak_ptr 引用 std::make_shared 所創建的控制塊,那麼 T 所占有的內存持續存在,直至所有弱擁有者亦被銷毀,在 sizeof(T) 較大時這可能是不想要的行為。
  • 如果在可訪問 T 的非公開構造函數的語境中執行,那麼 std::shared_ptr<T>(new T(args...)) 可能會調用它,而 std::make_shared 要求對被選擇構造函數的公開訪問。
  • 不同於 std::shared_ptr 構造函數,std::make_shared 不允許自定義刪除器。
  • std::make_shared 使用 ::new,因此如果用類特定的 operator new 設置了任何特殊行為,那麼它將異於 std::shared_ptr<T>(new T(args...))
  • std::shared_ptr 支持數組類型(從 C++17 起),但 std::make_shared 不支持。boost::make_shared 支持此功能。
(C++20 前)
  • f(std::shared_ptr<int>(new int(42)), g()) 的代碼,如果 gnew int(42) 後得到調用且拋出異常,那麼可能導致內存泄漏,而 f(std::make_shared<int>(42), g()) 是安全的,因為兩次函數調用決不會穿插
(C++17 前)

構造函數以 U* 類型指針 ptr 啟用 shared_from_this,表示它確定 U 是否擁有作為 std::enable_shared_from_this 特化的無歧義且可訪問(C++17 起)基類,在是的情況下會求值 if (ptr != nullptr && ptr->weak_this .expired())
    ptr->weak_this = std::shared_ptr<std::remove_cv_t<U>>
        (*this, const_cast<std::remove_cv_t<U>*>(ptr));

weak_this 成員的賦值不是原子的,且與任何到同一對象的潛在並發訪問衝突。這確保將來對 shared_from_this() 的調用,將與此裸指針構造函數所創建的 std::shared_ptr 共享所有權。

上述代碼中,測試 ptr->weak_this .expired() 是為確保當 weak_this 指示已有所有者時無須對它重賦值。從 C++17 起要求此測試。

功能特性測試 標準 功能特性
__cpp_lib_shared_ptr_arrays 201707L (C++20) std::make_shared 的數組支持;重載 (2-5)
__cpp_lib_smart_ptr_for_overwrite 202002L (C++20) 進行默認初始化的智能指針創建 (std::allocate_shared_for_overwritestd::make_shared_for_overwritestd::make_unique_for_overwrite);重載 (6,7)

示例

#include <iostream>
#include <memory>
#include <type_traits>
#include <vector>

struct C
{
    // < 需要构造函数 (C++20 前)
    C(int i) : i(i) {}
    C(int i, float f) : i(i), f(f) {}
    int i;
    float f{};
};

int main()
{
    // 为 “sp1” 的类型使用 “auto”
    auto sp1 = std::make_shared<C>(1); // 重载 (1)
    static_assert(std::is_same_v<decltype(sp1), std::shared_ptr<C>>);
    std::cout << "sp1->{ i:" << sp1->i << ", f:" << sp1->f << " }\n";
    
    // 明确 “sp2” 的类型
    std::shared_ptr<C> sp2 = std::make_shared<C>(2, 3.0f); // 重载 (1)
    static_assert(std::is_same_v<decltype(sp2), std::shared_ptr<C>>);
    static_assert(std::is_same_v<decltype(sp1), decltype(sp2)>);
    std::cout << "sp2->{ i:" << sp2->i << ", f:" << sp2->f << " }\n";
    
    // 指向值初始化的 float[64] 的 shared_ptr;重载 (2):
    std::shared_ptr<float[]> sp3 = std::make_shared<float[]>(64);
    
    // 指向值初始化的 long[5][3][4] 的 shared_ptr;重载 (2):
    std::shared_ptr<long[][3][4]> sp4 = std::make_shared<long[][3][4]>(5);
    
    // 指向值初始化的 short[128] 的 shared_ptr;重载 (3):
    std::shared_ptr<short[128]> sp5 = std::make_shared<short[128]>();
    
    // 指向值初始化的 int[7][6][5] 的 shared_ptr;重载 (3):
    std::shared_ptr<int[7][6][5]> sp6 = std::make_shared<int[7][6][5]>();
    
    // 指向 double[256] 的 shared_ptr,其各元素均为 2.0;重载 (4):
    std::shared_ptr<double[]> sp7 = std::make_shared<double[]>(256, 2.0);
    
    // 指向 double[7][2] 的 shared_ptr,其各 double[2] 元素均为 {3.0, 4.0};重载 (4):
    std::shared_ptr<double[][2]> sp8 = std::make_shared<double[][2]>(7, {3.0, 4.0});
    
    // 指向 vector<int>[4] 的 shared_ptr,其各向量的内容均为 {5, 6};重载 (4):
    std::shared_ptr<std::vector<int>[]> sp9 =
        std::make_shared<std::vector<int>[]>(4, {5, 6});
    
    // 指向 float[512] 的 shared_ptr,其各元素均为 1.0;重载 (5):
    std::shared_ptr<float[512]> spA = std::make_shared<float[512]>(1.0);
    
    // 指向 double[6][2] 的 shared_ptr,其各 double[2] 元素均为 {1.0, 2.0};重载 (5):
    std::shared_ptr<double[6][2]> spB = std::make_shared<double[6][2]>({1.0, 2.0});
    
    // 指向 vector<int>[4] 的 shared_ptr,其各向量的内容均为 {5, 6};重载 (5):
    std::shared_ptr<std::vector<int>[4]> spC =
        std::make_shared<std::vector<int>[4]>({5, 6});
}

輸出:

sp1->{ i:1, f:0 }
sp2->{ i:2, f:3 }

缺陷報告

下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。

缺陷報告 應用於 出版時的行為 正確行為
LWG 4024 C++20 不明確如何銷毀 std::make_shared_for_overwrite 中構造的對象 使之明確

參閱

構造新的 shared_ptr
(公開成員函數) [編輯]
創建管理一個用分配器分配的新對象的共享指針
(函數模板) [編輯]
允許對象創建指代自身的 shared_ptr
(類模板) [編輯]
創建管理一個新對象的獨占指針
(函數模板) [編輯]
分配函數
(函數) [編輯]