Buscar..


Sintaxis

  • 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, ...));

Observaciones

C ++ no es un lenguaje gestionado por la memoria. La memoria asignada dinámicamente (es decir, los objetos creados con new ) se "filtrará" si no se desasigna explícitamente (con delete ). Es responsabilidad del programador asegurarse de que la memoria asignada dinámicamente se libere antes de descartar el último puntero a ese objeto.

Los punteros inteligentes se pueden usar para administrar automáticamente el alcance de la memoria asignada dinámicamente (es decir, cuando la última referencia del puntero se sale del alcance, se elimina).

Los punteros inteligentes se prefieren a los punteros "sin procesar" en la mayoría de los casos. Hacen explícita la semántica de propiedad de la memoria asignada dinámicamente, comunicando en sus nombres si se pretende que un objeto sea compartido o de propiedad única.

Use #include <memory> para poder usar punteros inteligentes.

Compartir propiedad (std :: shared_ptr)

La plantilla de clase std::shared_ptr define un puntero compartido que puede compartir la propiedad de un objeto con otros punteros compartidos. Esto contrasta con std::unique_ptr que representa propiedad exclusiva.

El comportamiento de compartir se implementa a través de una técnica conocida como conteo de referencias, donde el número de punteros compartidos que apuntan al objeto se almacena a su lado. Cuando este conteo llega a cero, ya sea a través de la destrucción o la reasignación de la última instancia de std::shared_ptr , el objeto se destruye automáticamente.


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

Para crear varios punteros inteligentes que compartan el mismo objeto, necesitamos crear otro shared_ptr que shared_ptr alias al primer puntero compartido. Aquí hay 2 formas de hacerlo:

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

Cualquiera de las formas anteriores convierte a secondShared un puntero compartido que comparte la propiedad de nuestra instancia de Foo con firstShared .

El puntero inteligente funciona como un puntero en bruto. Esto significa que puedes usar * para desreferenciarlos. El operador regular -> funciona:

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

Finalmente, cuando el último alias shared_ptr sale del ámbito, se llama al destructor de nuestra instancia de Foo .

Advertencia: la construcción de shared_ptr puede shared_ptr una excepción bad_alloc cuando se deben asignar datos adicionales para la semántica de propiedad compartida. Si al constructor se le pasa un puntero normal, se supone que posee el objeto apuntado y llama al eliminador si se produce una excepción. Esto significa que shared_ptr<T>(new T(args)) no perderá un objeto T si shared_ptr<T> asignación de shared_ptr<T> . Sin embargo, es recomendable utilizar make_shared<T>(args) o allocate_shared<T>(alloc, args) , que permiten a la implementación optimizar la asignación de memoria.


Asignación de matrices ([]) utilizando shared_ptr

C ++ 11 C ++ 17

Desafortunadamente, no hay una forma directa de asignar Arrays usando make_shared<> .

Es posible crear matrices para shared_ptr<> usando new y std::default_delete .

Por ejemplo, para asignar una matriz de 10 enteros, podemos escribir el código como

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

Aquí es obligatorio especificar std::default_delete para asegurarse de que la memoria asignada se limpie correctamente utilizando delete[] .

Si conocemos el tamaño en tiempo de compilación, podemos hacerlo de esta manera:

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>{}(); }

luego make_shared_array<int[10]> devuelve un shared_ptr<int> apunta a 10 ints, todo construido por defecto.

C ++ 17

Con C ++ 17, shared_ptr obtuvo soporte especial para tipos de arreglos. Ya no es necesario especificar el eliminador de matriz de forma explícita, y el puntero compartido se puede anular mediante el operador de índice de matriz [] :

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

Los punteros compartidos pueden apuntar a un subobjeto del objeto que posee:

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

Tanto p2 como p1 poseen el objeto de tipo Foo , pero p2 apunta a su miembro int x . Esto significa que si p1 queda fuera del alcance o se reasigna, el objeto Foo subyacente seguirá vivo, asegurándose de que p2 no cuelgue.


Importante: un shared_ptr solo se conoce a sí mismo y a todos los otros shared_ptr que se crearon con el constructor de alias. No conoce ningún otro puntero, incluidos todos los otros shared_ptr s creados con una referencia a la misma instancia de 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!!

Propiedad Transferencia de shared_ptr

De forma predeterminada, shared_ptr incrementa el recuento de referencia y no transfiere la propiedad. Sin embargo, se puede hacer para transferir la propiedad usando 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

Compartir con propiedad temporal (std :: weak_ptr)

Las instancias de std::weak_ptr pueden apuntar a objetos que son propiedad de instancias de std::shared_ptr y solo se convierten en propietarios temporales. Esto significa que los punteros débiles no alteran el recuento de referencias del objeto y, por lo tanto, no impiden la eliminación de un objeto si todos los punteros compartidos del objeto se reasignan o destruyen.


En el siguiente ejemplo, se utilizan instancias de std::weak_ptr para que la destrucción de un objeto de árbol no se inhiba:

#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();
}

A medida que los nodos secundarios se agregan a los secundarios del nodo raíz, su parent std::weak_ptr miembro se establece en el nodo raíz. El parent miembro se declara como un puntero débil en lugar de un puntero compartido, de manera que el recuento de referencia del nodo raíz no se incrementa. Cuando el nodo raíz se reinicia al final de main() , la raíz se destruye. Dado que el único resto de std::shared_ptr referencias a los nodos secundarios estaban contenidos en la colección de la raíz children , todos los nodos hijos son posteriormente destruidos también.

Debido a los detalles de la implementación del bloque de control, la memoria asignada shared_ptr puede no liberarse hasta que el shared_ptr referencia weak_ptr y el weak_ptr referencia weak_ptr lleguen a cero.

#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)
}

Dado que std::weak_ptr no mantiene vivo su objeto referenciado, no es posible el acceso directo de datos a través de std::weak_ptr . En su lugar, proporciona una función miembro lock() que intenta recuperar un std::shared_ptr para el objeto al que se hace referencia:

#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());
     }
}

Propiedad única (std :: unique_ptr)

C ++ 11

A std::unique_ptr es una plantilla de clase que administra la vida útil de un objeto almacenado dinámicamente. A diferencia de std::shared_ptr , el objeto dinámico es propiedad de solo una instancia de std::unique_ptr en cualquier momento,


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

(Nota: std::unique_ptr está disponible desde C ++ 11 y std::make_unique desde C ++ 14).

Sólo la variable ptr mantiene un puntero a un int asignado dinámicamente. Cuando un puntero único que posee un objeto queda fuera del alcance, el objeto propio se elimina, es decir, se llama a su destructor si el objeto es de clase y se libera la memoria para ese objeto.

Para usar std::unique_ptr y std::make_unique con tipos de matriz, use sus especializaciones de matriz:

// 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);

Puede acceder a std::unique_ptr como un puntero en bruto, ya que sobrecarga a esos operadores.


Puede transferir la propiedad del contenido de un puntero inteligente a otro puntero utilizando std::move , lo que hará que el puntero inteligente original apunte a 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)

Pasando unique_ptr a funciones como parámetro:

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))

Devolviendo unique_ptr desde funciones. Esta es la forma preferida de C ++ 11 de escribir funciones de fábrica, ya que transmite claramente la semántica de propiedad de la devolución: la persona que llama posee el unique_ptr resultante y es responsable de ello.

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

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

Compara esto con:

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

La plantilla de clase make_unique se proporciona desde C ++ 14. Es fácil agregarlo manualmente al código 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

A diferencia del puntero inteligente tonto ( std::auto_ptr ), unique_ptr también se puede crear una instancia con asignación de vectores ( no std::vector ). Los ejemplos anteriores fueron para asignaciones escalares . Por ejemplo, para tener una matriz de enteros asignada dinámicamente para 10 elementos, debe especificar int[] como tipo de plantilla (y no solo int ):

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

Que se puede simplificar con:

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

Ahora, usas arr_ptr como si fuera una matriz:

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

No tiene que preocuparse por la desasignación. Esta plantilla especializada de la versión llama a los constructores y destructores adecuadamente. El uso de la versión vectorizada de unique_ptr o un vector sí mismo es una opción personal.

En versiones anteriores a C ++ 11, std::auto_ptr estaba disponible. A diferencia de unique_ptr , se permite copiar auto_ptr s, sobre el cual la fuente ptr perderá la propiedad del puntero contenido y el destino lo recibirá.

Uso de eliminaciones personalizadas para crear una envoltura para una interfaz C

Muchas interfaces C, como SDL2, tienen sus propias funciones de eliminación. Esto significa que no puede utilizar punteros inteligentes directamente:

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

En su lugar, necesita definir su propio deleter. Los ejemplos aquí utilizan la estructura SDL_Surface que debería liberarse utilizando la función SDL_FreeSurface() , pero deberían ser adaptables a muchas otras interfaces en C.

El eliminador debe ser invocable con un argumento de puntero, y por lo tanto puede ser, por ejemplo, un puntero de función simple:

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

Cualquier otro objeto llamable también funcionará, por ejemplo, una clase con un 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

Esto no solo le proporciona una administración de memoria automática segura y sin sobrecarga (si utiliza unique_ptr ), unique_ptr que también obtiene una seguridad excepcional.

Tenga en cuenta que el borrado es parte del tipo para unique_ptr , y la implementación puede usar la optimización de la base vacía para evitar cualquier cambio en el tamaño de los borradores personalizados vacíos. Entonces, mientras std::unique_ptr<SDL_Surface, SurfaceDeleter> y std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> resuelven el mismo problema de una manera similar, el primer tipo sigue siendo solo del tamaño de un puntero mientras que este último tipo debe contener dos punteros: tanto la SDL_Surface* como la función de puntero! Cuando se tienen eliminaciones personalizadas de función libre, es preferible envolver la función en un tipo vacío.

En los casos en que el conteo de referencias es importante, se podría usar un shared_ptr lugar de un unique_ptr . shared_ptr siempre almacena un eliminador, esto borra el tipo del eliminador, lo que podría ser útil en las API. Las desventajas de usar shared_ptr sobre unique_ptr incluyen un mayor costo de memoria para almacenar el eliminador y un costo de rendimiento para mantener el recuento de referencia.

// 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

Con la template auto , podemos hacer que sea aún más fácil envolver nuestros borrados personalizados:

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>>;

Con lo que el ejemplo anterior es simplemente:

unique_ptr_deleter<SDL_Surface, SDL_FreeSurface> c(pointer);

Aquí, el propósito de auto es manejar todas las funciones libres, ya sea que devuelvan void (por ejemplo, SDL_FreeSurface ) o no (por ejemplo, fclose ).

Propiedad única sin semántica de movimiento (auto_ptr)

C ++ 11

NOTA: std::auto_ptr ha quedado en desuso en C ++ 11 y se eliminará en C ++ 17. Solo debe usar esto si está obligado a usar C ++ 03 o anterior y está dispuesto a ser cuidadoso. Se recomienda pasar a unique_ptr en combinación con std::move para reemplazar el comportamiento de std::auto_ptr .

Antes de tener std::unique_ptr , antes de mover la semántica, teníamos std::auto_ptr . std::auto_ptr proporciona propiedad única pero transfiere la propiedad en la copia.

Al igual que con todos los punteros inteligentes, std::auto_ptr limpia automáticamente los recursos (ver RAII ):

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

pero solo permite un dueño:

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

Esto permite usar std :: auto_ptr para mantener la propiedad explícita y única ante el peligro de perder la propiedad sin querer:

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

La transferencia de propiedad ocurrió en el constructor "copia". El constructor de copia y el operador de asignación de copia de auto_ptr toman sus operandos por referencia no const para que puedan ser modificados. Una implementación de ejemplo podría ser:

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 ... */
};

Esto rompe la semántica de la copia, que requiere que copiar un objeto te deje con dos versiones equivalentes. Para cualquier tipo de copia, T , debería poder escribir:

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

Pero para auto_ptr , este no es el caso. Como resultado, no es seguro poner auto_ptr s en contenedores.

Consiguiendo un share_ptr refiriéndose a esto

enable_shared_from_this permite obtener una instancia de shared_ptr válida para this .

Al derivar su clase de la plantilla de clase enable_shared_from_this , usted hereda un método shared_from_this que devuelve una instancia de shared_ptr a this .

Tenga en cuenta que el objeto debe crearse como shared_ptr en primer lugar:

#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

Nota (2) no puede llamar a enable_shared_from_this dentro del constructor.

#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();
    ...
}

Si usa shared_from_this() en un objeto que no es propiedad de shared_ptr , como un objeto automático local o un objeto global, el comportamiento no está definido. Desde C ++ 17 lanza std::bad_alloc en std::bad_alloc lugar.

El uso de shared_from_this() de un constructor es equivalente a usarlo en un objeto que no pertenece a shared_ptr , porque los objetos son poseídos por shared_ptr después de que el constructor regrese.

Casting std :: shared_ptr pointers

No es posible usar directamente static_cast , const_cast , dynamic_cast y reinterpret_cast en std::shared_ptr para recuperar un puntero que comparte la propiedad con el puntero que se pasa como argumento. En su lugar, deben utilizarse las funciones std::static_pointer_cast , std::const_pointer_cast , std::dynamic_pointer_cast y 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));

Tenga en cuenta que std::reinterpret_pointer_cast no está disponible en C ++ 11 y C ++ 14, ya que solo fue propuesto por N3920 y adoptado en Library Fundamentals TS en febrero de 2014 . Sin embargo, se puede implementar de la siguiente manera:

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())); }

Escribiendo un puntero inteligente: value_ptr

Un value_ptr es un puntero inteligente que se comporta como un valor. Cuando se copia, copia su contenido. Cuando se crea, crea su contenido.

// 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)...)};
}

Este valor_ptr en particular solo está vacío si lo construyes con empty_ptr_t o si te mueves de él. Expone el hecho de que es un unique_ptr , por explicit operator bool() const trabaja en él. .get() se ha cambiado para devolver una referencia (ya que casi nunca está vacía), y .get_pointer() devuelve un puntero.

Este puntero inteligente puede ser útil para los casos pImpl , donde queremos valores semánticos, pero tampoco queremos exponer los contenidos de pImpl fuera del archivo de implementación.

Con una Copier no predeterminada, incluso puede manejar clases de base virtual que saben cómo producir instancias de sus derivadas y convertirlas en tipos de valor.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow