Spec-Zone.ru › C++

std::allocate_shared, std::allocate_shared_for_overwrite

Определено в заголовочном файле <memory>
template< class T, class Alloc, class... Args >
shared_ptr<T> allocate_shared( const Alloc& alloc, Args&&... args );
(1) (начиная с C++11)
(constexpr начиная с C++26)
template< class T, class Alloc >
shared_ptr<T> allocate_shared( const Alloc& alloc, std::size_t N );
(2) (начиная с C++20)
(constexpr начиная с C++26)
template< class T, class Alloc >
shared_ptr<T> allocate_shared( const Alloc& alloc );
(3) (начиная с C++20)
(constexpr начиная с C++26)
template< class T, class Alloc >
shared_ptr<T> allocate_shared( const Alloc& alloc, std::size_t N,
                               const std::remove_extent_t<T>& u );
(4) (начиная с C++20)
(constexpr начиная с C++26)
template< class T, class Alloc >
shared_ptr<T> allocate_shared( const Alloc& alloc,
                               const std::remove_extent_t<T>& u );
(5) (начиная с C++20)
(constexpr начиная с C++26)
template< class T, class Alloc >
shared_ptr<T> allocate_shared_for_overwrite( const Alloc& alloc );
(6) (начиная с C++20)
(constexpr начиная с C++26)
template< class T, class Alloc >
shared_ptr<T> allocate_shared_for_overwrite( const Alloc& alloc,
                                             std::size_t N );
(7) (начиная с C++20)
(constexpr начиная с C++26)

Выделяет память для объекта с помощью копии alloc (перепривязанной к неопределённому value_type) и инициализирует объект переданными аргументами. Возвращает объект std::shared_ptr, управляющий созданным объектом.

1) Объект имеет тип T и конструируется так, как если бы был вызван std::allocator_traits<Alloc>::construct(a, pt, (std::forward<Args>(args)...), где pt — указатель std::remove_cv_t<T>* на хранилище, подходящее для размещения объекта типа std::remove_cv_t<T>. Если объект подлежит уничтожению, он уничтожается так, как если бы был вызван std::allocator_traits<Alloc>::destroy(a, pt), где pt — указатель на этот объект типа std::remove_cv_t<T>.
В приведённом выше описании a имеет тип Alloc и является копией alloc, возможно перепривязанной.

Эта перегрузка участвует в разрешении перегрузки, только если 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 является массивом неизвестной границы.

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

В приведённом ниже описании a имеет тип Alloc и является копией alloc, возможно перепривязанной.

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

  • Если U не является типом массива, каждый элемент конструируется так, как если бы было вычислено следующее выражение, где pu — указатель std::remove_cv_t<U>* на хранилище, подходящее для размещения объекта типа std::remove_cv_t<U>, а pv — указатель void* на хранилище, подходящее для размещения объекта типа U:
2,3) std::allocator_traits<Alloc>::construct(a, pu) 4,5) std::allocator_traits<Alloc>::construct(a, pu, u) 6,7) ::new (pv) U
  • В противном случае рекурсивно инициализируются элементы каждого элемента. Для следующего измерения:
    • U становится std::remove_extent_t<U>.
    • Для перегрузок (4,5) u становится соответствующим элементом u.

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

Каждый уничтожаемый элемент массива не-массивного типа U уничтожается так, как если бы было вычислено следующее выражение:

2-5) std::allocator_traits<Alloc>::destroy(a, pu), где pu — указатель U* на этот элемент массива типа U 6,7) pu->~U(), где pu — указатель на этот элемент массива типа U
(начиная с C++20)

Параметры

alloc - используемый аллокатор
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.

Исключения

Могут быть выброшены исключения, выбрасываемые из Alloc::allocate() или конструктора T. Если выбрасывается исключение, (1) не оказывает эффекта. Если при конструировании массива выбрасывается исключение, уже инициализированные элементы уничтожаются в обратном порядке(начиная с C++20).

Примечания

Эти функции обычно выделяют больше памяти, чем sizeof(T), чтобы разместить внутренние структуры учёта, например счётчики ссылок.

Как и std::make_shared, эта функция обычно выполняет только одно выделение памяти и размещает объект T и управляющий блок в выделенном блоке памяти (стандарт рекомендует, но не требует этого; так поступают все известные реализации). Копия alloc хранится в управляющем блоке, чтобы её можно было использовать для освобождения памяти после обнуления счётчиков совместных и слабых ссылок.

В отличие от std::shared_ptr constructors, std::allocate_shared не принимает отдельный пользовательский делетер: переданный аллокатор используется для уничтожения управляющего блока и объекта T, а также для освобождения общего блока памяти, в котором они размещены.

std::shared_ptr поддерживает типы массивов (начиная с C++17), а std::allocate_shared — нет. Эту функциональность поддерживает boost::allocate_shared.

(до C++20)

Конструктор включает 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_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 <cstddef>
#include <iostream>
#include <memory>
#include <memory_resource>
#include <vector>

class Value
{
    int i;
public:
    Value(int i) : i(i) { std::cout << "Value(), i = " << i << '\n'; }
    ~Value() { std::cout << "~Value(), i = " << i << '\n'; }
    void print() const { std::cout << "i = " << i << '\n'; }
};

int main()
{
    // Create a polymorphic allocator using the monotonic buffer resource
    std::byte buffer[sizeof(Value) * 8];
    std::pmr::monotonic_buffer_resource resource(buffer, sizeof(buffer));
    std::pmr::polymorphic_allocator<Value> allocator(&resource);
    
    std::vector<std::shared_ptr<Value>> v;
    
    for (int i{}; i != 4; ++i)
        // Use std::allocate_shared with the custom allocator
        v.emplace_back(std::allocate_shared<Value>(allocator, i));
    
    for (const auto& sp : v)
        sp->print();
} //< All shared pointers will automatically clean up when they go out of scope.

Вывод:

Value(), i = 0
Value(), i = 1
Value(), i = 2
Value(), i = 3
i = 0
i = 1
i = 2
i = 3
~Value(), i = 0
~Value(), i = 1
~Value(), i = 2
~Value(), i = 3

Отчёты об ошибках

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

Отчёт об ошибке Применён к Опубликованное поведение Корректное поведение
LWG 3216 C++20 std::allocate_shared всегда перепривязывал аллокатор
перед конструированием и уничтожением объектов
перепривязка необязательна
LWG 4024 C++20 было неясно, как уничтожаются объекты, сконструированные в
std::allocate_shared_for_overwrite
уточнено

См. также

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

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

Spec-Zone.ru

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