名前空間
変種
操作

std::make_shared, std::make_shared_for_overwrite

From cppreference.com
< cpp‎ | memory‎ | shared ptr
 
 
メモリ管理ライブラリ
(説明用*)
未初期化メモリのアルゴリズム
(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... 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
  • それ以外の場合、各要素の要素を再帰的に初期化します。次の次元の場合
  • Ustd::remove_extent_t<U> になります。
  • オーバーロード (4,5) の場合、uu の対応する要素になります。

返された std::shared_ptr によって管理されるオブジェクトの寿命が終了したとき、または配列要素の初期化中に例外がスローされたとき、初期化された要素は元の構築とは逆の順序で破棄されます。

破棄される非配列型 U の各配列要素は、pu->~U() のように破棄されます。ここで pu は型 U のその配列要素へのポインタです。

(C++20以降)

[編集] パラメータ

args - T のオブジェクトが構築される引数のリスト
N - 使用する配列サイズ
u - 配列のすべての要素を初期化するための初期値

[編集] 戻り値

T のオブジェクトへの std::shared_ptr または、T が非境界配列型の場合 std::remove_extent_t<T>[N](C++20以降)

返された std::shared_ptr r について、r.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...)) は少なくとも2回の割り当て (オブジェクト T 用と shared pointer のコントロールブロック用) を実行しますが、std::make_shared<T> は通常1回の割り当てのみを実行します (標準ではこれを推奨していますが、必須ではありません。既知の実装はすべてこれを行っています)。
  • すべての共有所有者の寿命が終了した後、std::weak_ptrstd::make_shared によって作成されたコントロールブロックを参照している場合、T によって占有されているメモリは、すべての弱い所有者も破棄されるまで存続します。これは、sizeof(T) が大きい場合に望ましくない可能性があります。
  • std::shared_ptr<T>(new T(args...)) は、アクセス可能なコンテキストで実行された場合、T の非公開コンストラクタを呼び出すことができますが、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()) のようなコードは、new int(42) の後に g が呼び出され、例外をスローした場合にメモリリークを引き起こす可能性がありますが、f(std::make_shared<int>(42), g()) は安全です。これは、2つの関数呼び出しが決して interleaved (割り込まれて実行) されないためです。
(C++17まで)

コンストラクタが型 U* のポインタ ptrshared_from_this を有効にする とは、Ustd::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_overwrite, std::make_shared_for_overwrite, std::make_unique_for_overwrite)。オーバーロード (6,7)

[編集]

#include <iostream>
#include <memory>
#include <type_traits>
#include <vector>
 
struct C
{
    // constructors needed (until C++20)
    C(int i) : i(i) {}
    C(int i, float f) : i(i), f(f) {}
    int i;
    float f{};
};
 
int main()
{
    // using “auto” for the type of “sp1”
    auto sp1 = std::make_shared<C>(1); // overload (1)
    static_assert(std::is_same_v<decltype(sp1), std::shared_ptr<C>>);
    std::cout << "sp1->{ i:" << sp1->i << ", f:" << sp1->f << " }\n";
 
    // being explicit with the type of “sp2”
    std::shared_ptr<C> sp2 = std::make_shared<C>(2, 3.0f); // overload (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";
 
    // shared_ptr to a value-initialized float[64]; overload (2):
    std::shared_ptr<float[]> sp3 = std::make_shared<float[]>(64);
 
    // shared_ptr to a value-initialized long[5][3][4]; overload (2):
    std::shared_ptr<long[][3][4]> sp4 = std::make_shared<long[][3][4]>(5);
 
    // shared_ptr to a value-initialized short[128]; overload (3):
    std::shared_ptr<short[128]> sp5 = std::make_shared<short[128]>();
 
    // shared_ptr to a value-initialized int[7][6][5]; overload (3):
    std::shared_ptr<int[7][6][5]> sp6 = std::make_shared<int[7][6][5]>();
 
    // shared_ptr to a double[256], where each element is 2.0; overload (4):
    std::shared_ptr<double[]> sp7 = std::make_shared<double[]>(256, 2.0);
 
    // shared_ptr to a double[7][2], where each double[2]
    // element is {3.0, 4.0}; overload (4):
    std::shared_ptr<double[][2]> sp8 = std::make_shared<double[][2]>(7, {3.0, 4.0});
 
    // shared_ptr to a vector<int>[4], where each vector
    // has contents {5, 6}; overload (4):
    std::shared_ptr<std::vector<int>[]> sp9 =
        std::make_shared<std::vector<int>[]>(4, {5, 6});
 
    // shared_ptr to a float[512], where each element is 1.0; overload (5):
    std::shared_ptr<float[512]> spA = std::make_shared<float[512]>(1.0);
 
    // shared_ptr to a double[6][2], where each double[2] element
    // is {1.0, 2.0}; overload (5):
    std::shared_ptr<double[6][2]> spB = std::make_shared<double[6][2]>({1.0, 2.0});
 
    // shared_ptr to a vector<int>[4], where each vector
    // has contents {5, 6}; overload (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++標準に遡って適用されました。

DR 適用対象 公開された動作 正しい動作
LWG 4024 C++20 構築されたオブジェクトがどのように
std::make_shared_for_overwrite で破棄されるかが不明確でした。
明確化された

[編集] 関連項目

新しい shared_ptr を構築する
(public メンバ関数) [編集]
アロケータを使用して割り当てられた新しいオブジェクトを管理する共有ポインタを作成する
(関数テンプレート) [編集]
オブジェクトが自身を参照する shared_ptr を作成できるようにします
(クラステンプレート) [編集]
新しいオブジェクトを管理する一意のポインタを作成する
(関数テンプレート) [編集]
メモリ割り当て関数
(関数) [編集]
English 日本語 中文(简体) 中文(繁體)