C++
Умные указатели
Поиск…
Синтаксис
-
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
К сожалению, нет прямого способа выделения массивов с использованием 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 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)
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.
Шаблон класса 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]()); }
В отличие от немого умного указателя ( 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);
С 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)
ПРИМЕЧАНИЕ: 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
он может обрабатывать виртуальные базовые классы, которые знают, как создавать экземпляры своих производных и превращать их в типы значений.