std::make_shared, std::make_shared_for_overwrite
Определён в заголовочном файле <memory> | ||
|---|---|---|
template< class T, class... Args > shared_ptr<T> make_shared( Args&&... args ); | (1) |
(начиная с C++11) (constexpr начиная с C++26) |
template< class T > shared_ptr<T> make_shared( std::size_t N ); | (2) |
(начиная с C++20) (constexpr начиная с C++26) |
template< class T > shared_ptr<T> make_shared(); | (3) |
(начиная с C++20) (constexpr начиная с C++26) |
template< class T > shared_ptr<T> make_shared( std::size_t N, const std::remove_extent_t<T>& u ); | (4) |
(начиная с C++20) (constexpr начиная с C++26) |
template< class T > shared_ptr<T> make_shared( const std::remove_extent_t<T>& u ); | (5) |
(начиная с C++20) (constexpr начиная с C++26) |
template< class T > shared_ptr<T> make_shared_for_overwrite(); | (6) |
(начиная с C++20) (constexpr начиная с C++26) |
template< class T > shared_ptr<T> make_shared_for_overwrite( std::size_t N ); | (7) |
(начиная с C++20) (constexpr начиная с C++26) |
Выделяет память для объекта и инициализирует его переданными аргументами. Возвращает объект std::shared_ptr, управляющий только что созданным объектом.
T и конструируется так, как если бы это было выполнено с помощью ::new (pv) T(std::forward<Args>(args)...), где pv — указатель void* на область памяти, подходящую для хранения объекта типа T. Если объект необходимо уничтожить, он уничтожается так, как если бы это было выполнено с помощью pt->~T(), где pt — указатель на этот объект типа T. | Эта перегрузка участвует в разрешении перегрузки только в том случае, если | (начиная с C++20) |
T является массивом неограниченного размера.T. Каждый элемент имеет значение по умолчанию.T является массивом фиксированного размера.u.T является массивом неограниченного размера.T. Каждый элемент имеет начальное значение u.T является массивом фиксированного размера.T. - Если
Tне является типом массива, объект конструируется так, как если бы это было выполнено с помощью::new (pv) T, гдеpv— указатель void* на область памяти, подходящую для хранения объекта типаT. Если объект необходимо уничтожить, он уничтожается так, как если бы это было выполнено с помощьюpt->~T(), гдеpt— указатель на этот объект типаT. - Если
Tявляется массивом фиксированного размера, начальное значение каждого элемента не определено.
T не является типом массива или является массивом фиксированного размера.T является массивом неограниченного размера. Инициализация и уничтожение элементов массиваЭлементы массива типа
::new (pv) U() 4,5) ::new (pv) U(u) 6,7) ::new (pv) U
Когда срок жизни объекта, которым управляет возвращаемый Каждый уничтожаемый элемент массива нескалярного типа | (начиная с C++20) |
Параметры
| args | - | список аргументов, с которыми будет сконструирован объект типа T |
| N | - | используемый размер массива |
| u | - | начальное значение для инициализации каждого элемента массива |
Возвращаемое значение
std::shared_ptr на объект типа T или std::remove_extent_t<T>[N], если T является массивом неограниченного размера(начиная с 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...))выполняет как минимум два выделения памяти (одно для объектаTи одно для управляющего блока разделяемого указателя), тогда какstd::make_shared<T>обычно выполняет только одно выделение памяти (стандарт рекомендует это, но не требует; все известные реализации поступают именно так). - Если какой-либо
std::weak_ptrссылается на управляющий блок, созданныйstd::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...)).
| (до C++20) |
| (до C++17) |
Конструктор «включает shared_from_this» при наличии указателя ptr типа U* означает, что он определяет, имеет ли U однозначный и доступный(начиная с C++17) базовый класс, являющийся специализацией std::enable_shared_from_this, и, если да, выполняет 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++.
| Отчёт об ошибке | Применён к | Опубликованное поведение | Исправленное поведение |
|---|---|---|---|
| LWG 4024 | C++20 | не было ясно, как уничтожаются объекты, сконструированные вstd::make_shared_for_overwrite | разъяснено |
См. также
конструирует новый shared_ptr (открытая функция-член) |
|
|
(C++20) | создаёт разделяемый указатель, управляющий новым объектом, выделенным с помощью аллокатора (шаблон функции) |
|
(C++11) | позволяет объекту создавать shared_ptr, ссылающийся на него самого (шаблон класса) |
|
(C++14)(C++20) | создаёт уникальный указатель, управляющий новым объектом (шаблон функции) |
| функции выделения памяти (функция) |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/memory/shared_ptr/make_shared