Namespaces
Variants

std:: make_shared, std:: make_shared_for_overwrite

From cppreference.net
Memory management library
( exposition only* )
Allocators
Uninitialized memory algorithms
Constrained uninitialized memory algorithms
Memory resources
Uninitialized storage (until C++20)
( until C++20* )
( until C++20* )
( until C++20* )

Garbage collector support (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
(C++11) (until C++23)
Определено в заголовочном файле <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 является указателем void * на хранилище, подходящее для размещения объекта типа T . Если объект должен быть уничтожен, он уничтожается как если бы с помощью pt - > ~T ( ) , где pt является указателем на этот объект типа T .

Эта перегрузка участвует в разрешении перегрузки только если T не является типом массива.

(since 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 . Если исключение выброшено, функции не имеют эффекта. Если исключение выброшено во время конструирования массива, уже проинициализированные элементы уничтожаются в обратном порядке. (since 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 после окончания времени жизни всех shared-владельцев, память, занятая T , сохраняется до тех пор, пока все weak-владельцы не будут уничтожены, что может быть нежелательно, если 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
{
    // конструкторы, необходимые (до 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 для value-инициализированного float[64]; перегрузка (2):
    std::shared_ptr<float[]> sp3 = std::make_shared<float[]>(64);
    // shared_ptr для value-инициализированного long[5][3][4]; перегрузка (2):
    std::shared_ptr<long[][3][4]> sp4 = std::make_shared<long[][3][4]>(5);
    // shared_ptr для value-initialized short[128]; перегрузка (3):
    std::shared_ptr<short[128]> sp5 = std::make_shared<short[128]>();
    // shared_ptr на value-инициализированный 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]
    // element is {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]
    // is {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 }

Отчеты о дефектах

Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены ретроактивно к ранее опубликованным стандартам C++.

DR Applied to Behavior as published Correct behavior
LWG 4024 C++20 было неясно, как уничтожаются объекты, созданные в
std::make_shared_for_overwrite
прояснено

Смотрите также

создает новый shared_ptr
(публичная функция-член)
создает умный указатель, который управляет новым объектом, выделенным с использованием аллокатора
(шаблон функции)
позволяет объекту создавать shared_ptr ссылающийся на себя
(шаблон класса)
создает уникальный указатель, который управляет новым объектом
(шаблон функции)
функции выделения памяти
(функция)