Spec-Zone.ru › C++

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[])
1) Создаёт объект типа 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)
2,3) То же, что (1), но создаваемый объект — это, возможно, многомерный массив, не-массивовые элементы которого типа std::remove_all_extents_t<T> инициализируются по умолчанию, как если бы по выражению placement-new ::new(pv) std::remove_all_extents_t<T>(). Перегрузка (2) создаёт массив размером N по первой размерности. Элементы массива инициализируются в порядке возрастания их адресов, а по окончании их жизненного цикла уничтожаются в обратном порядке их первоначального создания.
4,5) То же, что (2,3), но каждый элемент инициализируется значением по умолчанию u. Если U не является типом массива, то это выполняется так же, как по тому же выражению placement-new, что и в (1); в противном случае это выполняется так, как если бы каждый не-массивовый элемент (возможно многомерного) массива инициализировался соответствующим элементом из u с тем же выражением placement-new, что и в (1). Перегрузка (4) создаёт массив размером N по первой размерности. Элементы массива инициализируются в порядке возрастания их адресов, а по окончании их жизненного цикла уничтожаются в обратном порядке их первоначального создания.
6) То же, что (1), если T не является типом массива, и (3), если T является U[N], за исключением того, что создаваемый объект инициализируется по умолчанию.
7) То же, что (2), за исключением того, что отдельные элементы массива инициализируются по умолчанию.

В каждом случае объект (или отдельные элементы, если T является типом массива)(с C++20) будет уничтожен p->~X(), где p — указатель на объект и X — его тип.

Параметры

args - список аргументов, с помощью которых будет создан экземпляр T
N - размер массива
u - начальное значение для инициализации каждого элемента массива

Возвращаемое значение

Указатель на экземпляр типа 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...)).
  • std::shared_ptr поддерживает типы массивов (начиная с C++17), но std::make_shared этого не делает. Эта функциональность поддерживается boost::make_shared.
(до C++20)
  • код, например f(std::shared_ptr<int>(new int(42)), g()), может привести к утечке памяти, если g вызывается после new int(42) и генерирует исключение, в то время как f(std::make_shared<int>(42), g()) безопасен, так как два вызова функций никогда не переплетаются.
(до C++17)

Конструктор разрешает shared_from_this с указателем ptr типа U* означает, что он определяет, имеет ли U недвусмысленную и доступную базовый класс, который является специализацией 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 — скрытое изменяемое член 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 <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 }

См. также

(конструктор)
создаёт новый shared_ptr
(публичный член-функция)
allocate_sharedallocate_shared_for_overwrite
(C++20)
создаёт умный указатель, управляющий новым объектом, выделенным с помощью аллокатора
(шаблон функции)
enable_shared_from_this
(C++11)
позволяет объекту создать shared_ptr , ссылающийся на себя
(шаблон класса)
make_uniquemake_unique_for_overwrite
(C++14)(C++20)
создаёт уникальный указатель, управляющий новым объектом
(шаблон функции)
operator newoperator new[]
функции выделения памяти
(функция)

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API