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)
(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, управляющий только что созданным объектом.

1) Объект имеет тип T и конструируется так, как если бы это было выполнено с помощью ::new (pv) T(std::forward<Args>(args)...), где pv — указатель void* на область памяти, подходящую для хранения объекта типа T. Если объект необходимо уничтожить, он уничтожается так, как если бы это было выполнено с помощью 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 — указатель void* на область памяти, подходящую для хранения объекта типа T. Если объект необходимо уничтожить, он уничтожается так, как если бы это было выполнено с помощью pt->~T(), где pt — указатель на этот объект типа T.
  • Если T является массивом фиксированного размера, начальное значение каждого элемента не определено.
Эта перегрузка участвует в разрешении перегрузки только в том случае, если T не является типом массива или является массивом фиксированного размера.
7) Объект имеет тип std::remove_extent_t<T>[N]. Начальное значение каждого элемента не определено.
Эта перегрузка участвует в разрешении перегрузки только в том случае, если T является массивом неограниченного размера.

Инициализация и уничтожение элементов массива

Элементы массива типа U инициализируются в порядке возрастания их адресов.

  • Если U не является типом массива, каждый элемент конструируется так, как если бы это было выполнено с помощью следующего выражения, где pv — указатель void* на область памяти, подходящую для хранения объекта типа U:
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 становится соответствующим элементом u.

Когда срок жизни объекта, которым управляет возвращаемый std::shared_ptr, заканчивается или при инициализации элемента массива возникает исключение, инициализированные элементы уничтожаются в обратном порядке их создания.

Каждый уничтожаемый элемент массива нескалярного типа U уничтожается так, как если бы это было выполнено с помощью pu->~U(), где pu — указатель на этот элемент массива типа 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...)).
  • 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 однозначный и доступный(начиная с 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
(открытая функция-член)
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/cpp/memory/shared_ptr/make_shared

Spec-Zone.ru

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