std::make_shared, std::make_shared_for_overwrite
| Определено в заголовочном файле <memory>
|
||
template< class T, class... Args > shared_ptr<T> make_shared( Args&&... args ); |
(1) | (начиная с C++11) (T не массив) |
template< class T > shared_ptr<T> make_shared( std::size_t N ); |
(2) | (начиная с C++20) (T равен U[]) |
template< class T > shared_ptr<T> make_shared(); |
(3) | (начиная с C++20) (T равен U[N]) |
template< class T > shared_ptr<T> make_shared( std::size_t N, const std::remove_extent_t<T>& u ); |
(4) | (начиная с C++20) (T равен U[]) |
template< class T > shared_ptr<T> make_shared( const std::remove_extent_t<T>& u ); |
(5) | (начиная с C++20) (T равен U[N]) |
template< class T > shared_ptr<T> make_shared_for_overwrite(); |
(6) | (начиная с C++20) (T не равен U[]) |
template< class T > shared_ptr<T> make_shared_for_overwrite( std::size_t N ); |
(7) | (начиная с C++20) (T равен U[]) |
T и заключает его в std::shared_ptr, используя args в качестве списка параметров для конструктора T. Объект создаётся как бы выражением ::new (pv) T(std::forward<Args>(args)...), где pv это внутренний void* указатель на хранилище, подходящее для хранения объекта типа T. Хранилище обычно больше, чем sizeof(T), чтобы использовать одно выделение как для блока управления общего указателя, так и для объекта T. Конструктор std::shared_ptr, вызываемый этой функцией, включает shared_from_this с указателем на вновь созданный объект типа T. |
Эта перегрузка участвует в разрешении перегрузки, только если T не является типом массива |
(начиная с C++20) |
std::remove_all_extents_t<T> инициализируются значением, как если бы размещающим выражением new ::new(pv) std::remove_all_extents_t<T>(). Перегрузка (2) создаёт массив размером N по первому измерению. Элементы массива инициализируются в порядке возрастания их адресов, а когда их время жизни заканчивается, уничтожаются в порядке, обратном их первоначальному созданию.u. Если U не является типом массива, то это выполняется как бы с помощью того же размещающего выражения new, что и в (1); иначе это выполняется, как если бы каждый элемент массива (возможно, многомерного), не являющийся массивом, инициализировался соответствующим элементом из u тем же размещающим выражением new, что и в (1). Перегрузка (4) создаёт массив размером N по первому измерению. Элементы массива инициализируются в порядке возрастания их адресов, а когда их время жизни заканчивается, уничтожаются в порядке, обратном их первоначальному созданию.T не является типом массива, и (3), если T является U[N], за исключением того, что созданный объект инициализируется по умолчанию.В каждом случае объект (или отдельные элементы, если T является типом массива) (начиная с C++20) будет уничтожен p->~X(), где p это указатель на объект, а X его тип.
Параметры
| args | — | список аргументов, с которыми будет создан экземпляр T.
|
| N | — | размер массива |
| u | — | начальное значение для инициализации каждого элемента массива |
Возвращаемое значение
std::shared_ptr экземпляр типа T.
Исключения
Может генерировать std::bad_alloc или любое исключение, генерируемое конструктором T. Если возникает исключение, функции не имеют эффекта. Если при создании массива возникает исключение, уже инициализированные элементы уничтожаются в обратном порядке. (начиная с C++20)
Примечание
Эту функцию можно использовать как альтернативу 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 скрытый mutable элемент std::weak_ptr класса std::enable_shared_from_this. Присваивание элементу 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 <memory>
#include <vector>
#include <iostream>
#include <type_traits>
struct C
{
// необходимы конструкторы (до C++20)
C(int i) : i(i) {}
C(int i, float f) : i(i), f(f) {}
int i;
float f{};
};
int main()
{
// использование `auto` для типа `sp1`
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";
// shared_ptr на инициализированный значением float[64]; перегрузка (2):
std::shared_ptr<float[]> sp3 = std::make_shared<float[]>(64);
// shared_ptr на инициализированный значением long[5][3][4]; перегрузка (2):
std::shared_ptr<long[][3][4]> sp4 = std::make_shared<long[][3][4]>(5);
// shared_ptr на инициализированный значением short[128]; перегрузка (3):
std::shared_ptr<short[128]> sp5 = std::make_shared<short[128]>();
// shared_ptr на инициализированный значением int[7][6][5]; перегрузка (3):
std::shared_ptr<int[7][6][5]> sp6 = std::make_shared<int[7][6][5]>();
// shared_ptr на double[256], где каждый элемент равен 2.0; перегрузка (4):
std::shared_ptr<double[]> sp7 = std::make_shared<double[]>(256, 2.0);
// shared_ptr на double[7][2], где каждый элемент double[2]
// равен {3.0, 4.0}; перегрузка (4):
std::shared_ptr<double[][2]> sp8 = std::make_shared<double[][2]>(7, {3.0, 4.0});
// shared_ptr на vector<int>[4], где каждый вектор
// имеет содержимое {5, 6}; перегрузка (4):
std::shared_ptr<std::vector<int>[]> sp9 =
std::make_shared<std::vector<int>[]>(4, {5, 6});
// shared_ptr на float[512], где каждый элемент равен 1.0; перегрузка (5):
std::shared_ptr<float[512]> spA = std::make_shared<float[512]>(1.0);
// shared_ptr на double[6][2], где каждый double[2] элемент
// равен {1.0, 2.0}; перегрузка (5):
std::shared_ptr<double[6][2]> spB = std::make_shared<double[6][2]>({1.0, 2.0});
// shared_ptr на vector<int>[4], где каждый вектор
// имеет содержимое {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 }
Смотрите также
создаёт новый shared_ptr (public функция-элемент) | |
| создаёт общий указатель, который управляет новым объектом, выделенным с помощью аллокатора (шаблон функции) | |
(C++11) |
позволяет объекту создавать shared_ptr, ссылаясь на себя (шаблон класса) |
(C++14)(C++20) |
создаёт уникальный указатель, который управляет новым объектом (шаблон функции) |
| функции распределения памяти (функция) |