Поиск…


Синтаксис

  • std::shared_ptr<ClassType> variableName = std::make_shared<ClassType>(arg1, arg2, ...);
  • std::shared_ptr<ClassType> variableName (new ClassType(arg1, arg2, ...));
  • std::unique_ptr<ClassType> variableName = std::make_unique<ClassType>(arg1, arg2, ...); // C ++ 14
  • std::unique_ptr<ClassType> variableName (new ClassType(arg1, arg2, ...));

замечания

C ++ не является языком, управляемым памятью. Динамически выделенная память (т. Е. Объекты, созданные new ) будет «утечка», если она явно не освобождена (с delete ). Ответственность программиста заключается в том, чтобы освободить динамически выделенную память, прежде чем отбрасывать последний указатель на этот объект.

Умные указатели могут использоваться для автоматического управления областью динамически распределенной памяти (т. Е. Когда последняя ссылка указателя выходит за пределы области, она удаляется).

В большинстве случаев интеллектуальные указатели предпочтительнее «сырых» указателей. Они делают семантику владения динамически выделенной явной памятью, передавая в своих именах, должен ли объект быть общим или уникальным.

Используйте #include <memory> , чтобы использовать интеллектуальные указатели.

Совместное использование собственности (std :: shared_ptr)

Шаблон класса std::shared_ptr определяет общий указатель, который может делиться правами собственности на объект с другими общими указателями. Это контрастирует с std::unique_ptr который представляет собой эксклюзивное право собственности.

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


// Creation: 'firstShared' is a shared pointer for a new instance of 'Foo'
std::shared_ptr<Foo> firstShared = std::make_shared<Foo>(/*args*/);

Чтобы создать несколько интеллектуальных указателей, совместно использующих один и тот же объект, нам нужно создать еще один shared_ptr который сглаживает первый общий указатель. Вот два способа сделать это:

std::shared_ptr<Foo> secondShared(firstShared);  // 1st way: Copy constructing
std::shared_ptr<Foo> secondShared;
secondShared = firstShared;                      // 2nd way: Assigning

Любой из указанных способов делает secondShared общим указателем, который разделяет право собственности на наш экземпляр Foo с firstShared .

Умный указатель работает так же, как исходный указатель. Это означает, что вы можете использовать * чтобы разыменовать их. Оператор regular -> работает также:

secondShared->test(); // Calls Foo::test()

Наконец, когда последний aliased shared_ptr выходит из области видимости, вызывается деструктор нашего экземпляра Foo .

Предупреждение. При создании shared_ptr может bad_alloc исключение bad_alloc когда необходимо выделить дополнительные данные для семантики совместного использования. Если конструктор передается регулярным указателем, он предполагает, что ему принадлежит объект, на который указывает, и вызывает делеттер, если генерируется исключение. Это означает, что shared_ptr<T>(new T(args)) не будет утечка объекта T если выделение shared_ptr<T> не выполняется. Тем не менее, рекомендуется использовать make_shared<T>(args) или make_shared<T>(args) allocate_shared<T>(alloc, args) , которые позволяют реализации оптимизировать распределение памяти.


Выделение массивов ([]) с использованием shared_ptr

C ++ 11 C ++ 17

К сожалению, нет прямого способа выделения массивов с использованием make_shared<> .

Возможно создание массивов для shared_ptr<> с использованием new и std::default_delete .

Например, чтобы выделить массив из 10 целых чисел, мы можем написать код как

shared_ptr<int> sh(new int[10], std::default_delete<int[]>());

Указание std::default_delete является обязательным здесь, чтобы убедиться, что выделенная память правильно очищена с помощью delete[] .

Если мы знаем размер во время компиляции, мы можем сделать это следующим образом:

template<class Arr>
struct shared_array_maker {};
template<class T, std::size_t N>
struct shared_array_maker<T[N]> {
  std::shared_ptr<T> operator()const{
    auto r = std::make_shared<std::array<T,N>>();
    if (!r) return {};
    return {r.data(), r};
  }
};
template<class Arr>
auto make_shared_array()
-> decltype( shared_array_maker<Arr>{}() )
{ return shared_array_maker<Arr>{}(); }

то make_shared_array<int[10]> возвращает shared_ptr<int> указывающий на 10 ints по умолчанию.

C ++ 17

С C ++ 17 shared_ptr получила особую поддержку для типов массивов. Больше нет необходимости явно указывать элемент управления массивом, а общий указатель может быть разыменован с помощью оператора индекса массива [] :

std::shared_ptr<int[]> sh(new int[10]);
sh[0] = 42;

Общие указатели могут указывать на под-объект объекта, которому он владеет:

struct Foo { int x; };
std::shared_ptr<Foo> p1 = std::make_shared<Foo>();
std::shared_ptr<int> p2(p1, &p1->x);

Оба p2 и p1 принадлежат объекту типа Foo , но p2 указывает на его int member x . Это означает, что если p1 выходит за пределы области действия или переназначается, основной объект Foo будет по-прежнему оставаться в живых, гарантируя, что p2 не болтается.


Важно: shared_ptr знает только о себе и обо всех других shared_ptr которые были созданы с помощью конструктора псевдонимов. Он не знает о каких-либо других указателях, включая все другие shared_ptr s, созданные со ссылкой на тот же экземпляр Foo :

Foo *foo = new Foo;
std::shared_ptr<Foo> shared1(foo);
std::shared_ptr<Foo> shared2(foo); // don't do this

shared1.reset(); // this will delete foo, since shared1
                 // was the only shared_ptr that owned it

shared2->test(); // UNDEFINED BEHAVIOR: shared2's foo has been
                 // deleted already!!

Передача собственности shared_ptr

По умолчанию shared_ptr увеличивает счетчик ссылок и не передает права собственности. Тем не менее, это может быть сделано для передачи права собственности с помощью std::move :

shared_ptr<int> up = make_shared<int>();
// Transferring the ownership
shared_ptr<int> up2 = move(up);
// At this point, the reference count of up = 0 and the
// ownership of the pointer is solely with up2 with reference count = 1

Совместное использование с временным участием (std :: weak_ptr)

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


В следующем примере экземпляры std::weak_ptr используются так, что уничтожение древовидного объекта не блокируется:

#include <memory>
#include <vector>

struct TreeNode {
    std::weak_ptr<TreeNode> parent;
    std::vector< std::shared_ptr<TreeNode> > children;
};

int main() {
    // Create a TreeNode to serve as the root/parent.
    std::shared_ptr<TreeNode> root(new TreeNode);

    // Give the parent 100 child nodes.
    for (size_t i = 0; i < 100; ++i) {
        std::shared_ptr<TreeNode> child(new TreeNode);
        root->children.push_back(child);
        child->parent = root;
    }

    // Reset the root shared pointer, destroying the root object, and
    // subsequently its child nodes.
    root.reset();
}

Как дочерние узлы добавляются к детям корневого узла, их std::weak_ptr член parent устанавливается в корневой узел. parent член объявляется как слабый указатель, а не общий указатель, так что счетчик ссылок корневого узла не увеличивается. Когда корневой узел сбрасывается в конце main() , корень уничтожается. Так как единственные оставшиеся ссылки std::shared_ptr на дочерние узлы содержались в дочерних элементах корневого children , все дочерние узлы впоследствии также уничтожаются.

Из-за деталей реализации блока управления выделенная память shared_ptr может не быть выпущена до тех пор, пока счетчик ссылок shared_ptr и weak_ptr ссылок weak_ptr достигнут нуля.

#include <memory>
int main()
{
    {
         std::weak_ptr<int> wk;
         {
             // std::make_shared is optimized by allocating only once 
             // while std::shared_ptr<int>(new int(42)) allocates twice.
             // Drawback of std::make_shared is that control block is tied to our integer
             std::shared_ptr<int> sh = std::make_shared<int>(42);
             wk = sh;
             // sh memory should be released at this point...
         }
         // ... but wk is still alive and needs access to control block
     }
     // now memory is released (sh and wk)
}

Поскольку std::weak_ptr не сохраняет свой ссылочный объект в живых, прямой доступ к данным через std::weak_ptr невозможен. Вместо этого он предоставляет функцию-член lock() которая пытается получить std::shared_ptr к указанному объекту:

#include <cassert>
#include <memory>
int main()
{
    {
         std::weak_ptr<int> wk;
         std::shared_ptr<int> sp;
         {
             std::shared_ptr<int> sh = std::make_shared<int>(42);
             wk = sh;
             // calling lock will create a shared_ptr to the object referenced by wk
             sp = wk.lock();
             // sh will be destroyed after this point, but sp is still alive
         }
         // sp still keeps the data alive.
         // At this point we could even call lock() again 
         // to retrieve another shared_ptr to the same data from wk
         assert(*sp == 42);
         assert(!wk.expired());
         // resetting sp will delete the data,
         // as it is currently the last shared_ptr with ownership
         sp.reset();
         // attempting to lock wk now will return an empty shared_ptr,
         // as the data has already been deleted
         sp = wk.lock();
         assert(!sp);
         assert(wk.expired());
     }
}

Уникальное право собственности (std :: unique_ptr)

C ++ 11

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


// Creates a dynamic int with value of 20 owned by a unique pointer
std::unique_ptr<int> ptr = std::make_unique<int>(20);

(Примечание: std::unique_ptr доступен с C ++ 11 и std::make_unique с C ++ 14.)

Только переменная ptr содержит указатель на динамически выделенный int . Когда уникальный указатель, который владеет объектом, выходит из области видимости, принадлежащий ему объект удаляется, т. Е. Его деструктор вызывается, если объект имеет тип класса, и память для этого объекта освобождается.

Чтобы использовать std::unique_ptr и std::make_unique с типами массивов, используйте их специализации массивов:

// Creates a unique_ptr to an int with value 59
std::unique_ptr<int> ptr = std::make_unique<int>(59);

// Creates a unique_ptr to an array of 15 ints
std::unique_ptr<int[]> ptr = std::make_unique<int[]>(15);

Вы можете получить доступ к std::unique_ptr как к необработанному указателю, потому что он перегружает эти операторы.


Вы можете передать право собственности на содержимое смарт-указателя на другой указатель, используя std::move , что приведет к тому, что оригинальный интеллектуальный указатель укажет на nullptr .

// 1. std::unique_ptr
std::unique_ptr<int> ptr = std::make_unique<int>();

// Change value to 1
*ptr = 1;

// 2. std::unique_ptr (by moving 'ptr' to 'ptr2', 'ptr' doesn't own the object anymore)
std::unique_ptr<int> ptr2 = std::move(ptr);

int a = *ptr2; // 'a' is 1
int b = *ptr;  // undefined behavior! 'ptr' is 'nullptr'
               // (because of the move command above)

Передача unique_ptr в функции как параметр:

void foo(std::unique_ptr<int> ptr)
{
    // Your code goes here
}

std::unique_ptr<int> ptr = std::make_unique<int>(59);
foo(std::move(ptr))

Возвращение unique_ptr из функций. Это предпочтительный способ написания фабричных функций на C ++ 11, поскольку он ясно передает семантику права собственности: вызывающий пользователь получает результирующий unique_ptr и несет за это ответственность.

std::unique_ptr<int> foo()
{
    std::unique_ptr<int> ptr = std::make_unique<int>(59);
    return ptr;
}

std::unique_ptr<int> ptr = foo();

Сравните это с:

int* foo_cpp03();

int* p = foo_cpp03(); // do I own p? do I have to delete it at some point?
                      // it's not readily apparent what the answer is.
C ++ 14

Шаблон класса make_unique предоставляется с C ++ 14. Его легко добавить вручную в код C ++ 11:

template<typename T, typename... Args>
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(Args&&... args)
{ return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); }

// Use make_unique for arrays
template<typename T>
typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(size_t n)
{ return std::unique_ptr<T>(new typename std::remove_extent<T>::type[n]()); }
C ++ 11

В отличие от немого умного указателя ( std::auto_ptr ), unique_ptr также может быть создан с векторным распределением ( не std::vector ). Ранние примеры были для скалярных распределений. Например, чтобы иметь динамически выделенный целочисленный массив для 10 элементов, вы должны указать int[] как тип шаблона (а не просто int ):

std::unique_ptr<int[]> arr_ptr = std::make_unique<int[]>(10);

Что можно упростить с помощью:

auto arr_ptr = std::make_unique<int[]>(10);

Теперь вы используете arr_ptr как будто это массив:

arr_ptr[2] =  10; // Modify third element

Вам не нужно беспокоиться о де-распределении. Эта специализированная версия шаблона вызывает конструкторы и деструкторы соответственно. Использование векторной версии unique_ptr или самого vector - это личный выбор.

В версиях до C ++ 11 был доступен std::auto_ptr . В отличие от unique_ptr разрешено копировать auto_ptr s, после чего исходный ptr потеряет право собственности на содержащий указатель и получает его.

Использование пользовательских удалений для создания оболочки для интерфейса C

Многие интерфейсы C, такие как SDL2, имеют свои собственные функции удаления. Это означает, что вы не можете использовать интеллектуальные указатели напрямую:

std::unique_ptr<SDL_Surface> a; // won't work, UNSAFE!

Вместо этого вам нужно определить свой собственный дебит. В примерах здесь используется структура SDL_Surface которую следует освободить с помощью функции SDL_FreeSurface() , но они должны быть адаптированы ко многим другим C-интерфейсам.

Делектор должен быть вызван с аргументом указателя и, следовательно, может быть, например, простым указателем функции:

std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> a(pointer, SDL_FreeSurface);

Любой другой вызываемый объект также будет работать, например, класс с operator() :

struct SurfaceDeleter {
    void operator()(SDL_Surface* surf) {
        SDL_FreeSurface(surf);
    }
};

std::unique_ptr<SDL_Surface, SurfaceDeleter> a(pointer, SurfaceDeleter{}); // safe
std::unique_ptr<SDL_Surface, SurfaceDeleter> b(pointer); // equivalent to the above
                                                         // as the deleter is value-initialized

Это не только обеспечивает безопасное, новное накладные расходы (если вы используете unique_ptr ) автоматическое управление памятью, вы также получаете безопасность исключений.

Обратите внимание, что делетер является частью типа unique_ptr , и реализация может использовать пустую оптимизацию базы, чтобы избежать изменения размера для пустых пользовательских удалений. Таким образом, в то время как std::unique_ptr<SDL_Surface, SurfaceDeleter> и std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> решает одну и ту же проблему аналогичным образом, прежний тип по-прежнему является только размером указателя, в то время как последний тип должен содержать два указателя: и SDL_Surface* и указатель функции! При использовании произвольных пользовательских удалений функциональных возможностей предпочтительнее обернуть функцию в пустой тип.

В случаях, когда подсчет ссылок важен, можно использовать shared_ptr вместо unique_ptr . В shared_ptr всегда хранится deleter, это стирает тип делетера, который может быть полезен в API. Недостатки использования shared_ptr сравнению с unique_ptr включают в себя более высокую стоимость памяти для хранения дебетера и стоимость выполнения для поддержания счетчика ссылок.

// deleter required at construction time and is part of the type
std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> a(pointer, SDL_FreeSurface);

// deleter is only required at construction time, not part of the type
std::shared_ptr<SDL_Surface> b(pointer, SDL_FreeSurface); 
C ++ 17

С template auto мы можем сделать еще проще обернуть наши пользовательские удалители:

template <auto DeleteFn>
struct FunctionDeleter {
    template <class T>
    void operator()(T* ptr) {
        DeleteFn(ptr);
    }
};

template <class T, auto DeleteFn>
using unique_ptr_deleter = std::unique_ptr<T, FunctionDeleter<DeleteFn>>;

С помощью приведенного выше примера просто:

unique_ptr_deleter<SDL_Surface, SDL_FreeSurface> c(pointer);

Здесь цель auto - обрабатывать все свободные функции, независимо от того, возвращают ли они void (например, SDL_FreeSurface ) или нет (например, fclose ).

Уникальная собственность без семантики перемещения (auto_ptr)

C ++ 11

ПРИМЕЧАНИЕ: std::auto_ptr устарел на C ++ 11 и будет удален на C ++ 17. Вы должны использовать это только в том случае, если вы вынуждены использовать C ++ 03 или ранее и готовы быть осторожными. Рекомендуется перейти к unique_ptr в сочетании с std::move для замены поведения std::auto_ptr .

До того, как мы установили std::unique_ptr , перед семантикой перемещения была std::auto_ptr . std::auto_ptr предоставляет уникальное право собственности, но передает право собственности на копию.

Как и во всех умных указателях, std::auto_ptr автоматически очищает ресурсы (см. RAII ):

{
    std::auto_ptr<int> p(new int(42));
    std::cout << *p;
} // p is deleted here, no memory leaked

но допускает только одного владельца:

std::auto_ptr<X> px = ...;
std::auto_ptr<X> py = px; 
  // px is now empty 

Это позволяет использовать std :: auto_ptr, чтобы сохранить явное и уникальное право собственности на риск потери права собственности:

void f(std::auto_ptr<X> ) {
    // assumes ownership of X
    // deletes it at end of scope
};

std::auto_ptr<X> px = ...;
f(px); // f acquires ownership of underlying X
       // px is now empty
px->foo(); // NPE!
// px.~auto_ptr() does NOT delete

Передача права собственности произошла в конструкторе «copy». auto_ptr конструктор копирования «s и копировать оператор присваивания принимают операнды неисполнением const ссылки , так что они могут быть изменены. Пример реализации может быть:

template <typename T>
class auto_ptr {
    T* ptr;
public:
    auto_ptr(auto_ptr& rhs)
    : ptr(rhs.release())
    { }

    auto_ptr& operator=(auto_ptr& rhs) {
        reset(rhs.release());
        return *this;
    }

    T* release() {
        T* tmp = ptr;
        ptr = nullptr;
        return tmp;
    }

    void reset(T* tmp = nullptr) {
        if (ptr != tmp) {
            delete ptr;
            ptr = tmp;
        }
    }

    /* other functions ... */
};

Это нарушает семантику копирования, которая требует, чтобы копирование объекта оставило вас с двумя эквивалентными версиями. Для любого типа копирования T , я должен уметь писать:

T a = ...;
T b(a);
assert(b == a);

Но для auto_ptr это не так. В результате небезопасно вставлять auto_ptr s в контейнеры.

Получение shared_ptr, ссылающегося на это

enable_shared_from_this позволяет получить правильный shared_ptr экземпляр this .

При выводе своего класса из шаблона класса enable_shared_from_this , наследование метода shared_from_this , который возвращает shared_ptr экземпляра this .

Обратите внимание, что объект должен быть создан как shared_ptr на первом месте:

#include <memory>
class A: public enable_shared_from_this<A> {
};
A* ap1 =new A();
shared_ptr<A> ap2(ap1); // First prepare a shared pointer to the object and hold it!
// Then get a shared pointer to the object from the object itself
shared_ptr<A> ap3 = ap1->shared_from_this(); 
int c3 =ap3.use_count(); // =2: pointing to the same object

Примечание (2) вы не можете вызвать enable_shared_from_this внутри конструктора.

#include <memory> // enable_shared_from_this

class Widget : public std::enable_shared_from_this< Widget >
{
public:
    void DoSomething()
    {
        std::shared_ptr< Widget > self = shared_from_this();
        someEvent -> Register( self );
    }
private:
    ...
};

int main()
{
    ...
    auto w = std::make_shared< Widget >();
    w -> DoSomething();
    ...
}

Если вы используете shared_from_this() для объекта, не принадлежащего shared_ptr , такого как локальный автоматический объект или глобальный объект, тогда поведение не определено. Так как C ++ 17 вместо этого выбрасывает std::bad_alloc .

Использование shared_from_this() из конструктора эквивалентно использованию его на объекте, не принадлежащем shared_ptr , потому что объекты обладают shared_ptr после возврата конструктора.

Кастинг std :: shared_ptr указатели

Это не представляется возможным непосредственно использовать static_cast , const_cast , dynamic_cast и reinterpret_cast на std::shared_ptr для извлечения собственности обмена указатель с указателем передается в качестве аргумента. Вместо этого следует использовать функции std::static_pointer_cast , std::const_pointer_cast , std::dynamic_pointer_cast и std::reinterpret_pointer_cast :

struct Base { virtual ~Base() noexcept {}; };
struct Derived: Base {};
auto derivedPtr(std::make_shared<Derived>());
auto basePtr(std::static_pointer_cast<Base>(derivedPtr));
auto constBasePtr(std::const_pointer_cast<Base const>(basePtr));
auto constDerivedPtr(std::dynamic_pointer_cast<Derived const>(constBasePtr));

Обратите внимание, что std::reinterpret_pointer_cast недоступен в C ++ 11 и C ++ 14, поскольку он был предложен только N3920 и принят в TS Basic Library в феврале 2014 года . Однако его можно реализовать следующим образом:

template <typename To, typename From>
inline std::shared_ptr<To> reinterpret_pointer_cast(
    std::shared_ptr<From> const & ptr) noexcept
{ return std::shared_ptr<To>(ptr, reinterpret_cast<To *>(ptr.get())); }

Написание умного указателя: value_ptr

value_ptr - это умный указатель, который ведет себя как значение. При копировании копируется его содержимое. При создании создается его содержимое.

// Like std::default_delete:
template<class T>
struct default_copier {
  // a copier must handle a null T const* in and return null:
  T* operator()(T const* tin)const {
    if (!tin) return nullptr;
    return new T(*tin);
  }
  void operator()(void* dest, T const* tin)const {
    if (!tin) return;
    return new(dest) T(*tin);
  }
};
// tag class to handle empty case:
struct empty_ptr_t {};
constexpr empty_ptr_t empty_ptr{};
// the value pointer type itself:
template<class T, class Copier=default_copier<T>, class Deleter=std::default_delete<T>,
  class Base=std::unique_ptr<T, Deleter>
>
struct value_ptr:Base, private Copier {
  using copier_type=Copier;
  // also typedefs from unique_ptr

  using Base::Base;

  value_ptr( T const& t ):
    Base( std::make_unique<T>(t) ),
    Copier()
  {}
  value_ptr( T && t ):
    Base( std::make_unique<T>(std::move(t)) ),
    Copier()
  {}
  // almost-never-empty:
      value_ptr():
    Base( std::make_unique<T>() ),
    Copier()
  {}
  value_ptr( empty_ptr_t ) {}

  value_ptr( Base b, Copier c={} ):
    Base(std::move(b)),
    Copier(std::move(c))
  {}

  Copier const& get_copier() const {
    return *this;
  }

  value_ptr clone() const {
    return {
      Base(
        get_copier()(this->get()),
        this->get_deleter()
      ),
      get_copier()
    };
  }
  value_ptr(value_ptr&&)=default;
  value_ptr& operator=(value_ptr&&)=default;

  value_ptr(value_ptr const& o):value_ptr(o.clone()) {}
  value_ptr& operator=(value_ptr const&o) {
    if (o && *this) {
      // if we are both non-null, assign contents:
      **this = *o;
    } else {
      // otherwise, assign a clone (which could itself be null):
      *this = o.clone();
    }
    return *this;
  }
  value_ptr& operator=( T const& t ) {
    if (*this) {
      **this = t;
    } else {
      *this = value_ptr(t);
    }
    return *this;
  }
  value_ptr& operator=( T && t ) {
    if (*this) {
      **this = std::move(t);
    } else {
      *this = value_ptr(std::move(t));
    }
    return *this;
  }
  T& get() { return **this; }
  T const& get() const { return **this; }
  T* get_pointer() {
    if (!*this) return nullptr;
    return std::addressof(get());
  }
  T const* get_pointer() const {
    if (!*this) return nullptr;
    return std::addressof(get());
  }
  // operator-> from unique_ptr
};
template<class T, class...Args>
value_ptr<T> make_value_ptr( Args&&... args ) {
  return {std::make_unique<T>(std::forward<Args>(args)...)};
}

Этот конкретный value_ptr пуст, если вы создаете его с помощью empty_ptr_t или если вы перейдете от него. Он раскрывает тот факт, что это unique_ptr , поэтому на нем работает explicit operator bool() const . .get() был изменен, чтобы вернуть ссылку (поскольку она почти никогда не пуста), а .get_pointer() вместо этого возвращает указатель.

Этот интеллектуальный указатель может быть полезен для случаев pImpl , где нам нужна семантика значений, но мы также не хотим раскрывать содержимое pImpl вне файла реализации.

С помощью нестандартного Copier он может обрабатывать виртуальные базовые классы, которые знают, как создавать экземпляры своих производных и превращать их в типы значений.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow