Saltar al contenido

¿Qué es un puntero inteligente y cuándo debería usar uno?

Nuestros mejores investigadores han agotado sus provisiones de café, en su búsqueda todo el tiempo por la resolución, hasta que Benjamín halló la solución en Bitbucket así que en este momento la comparte contigo.

Solución:

ACTUALIZAR

Esta respuesta es bastante antigua, por lo que describe lo que era ‘bueno’ en ese momento, que eran punteros inteligentes proporcionados por la biblioteca Boost. Desde C ++ 11, la biblioteca estándar ha proporcionado suficientes tipos de punteros inteligentes, por lo que debe favorecer el uso de std::unique_ptr, std::shared_ptr y std::weak_ptr.

También había std::auto_ptr. Se parecía mucho a un puntero con mira, excepto que también tenía la peligrosa capacidad “especial” de ser copiado, lo que también transfiere inesperadamente la propiedad.
Quedó en desuso en C ++ 11 y se eliminó en C ++ 17, por lo que no deberías usarlo.

std::auto_ptr p1 (new MyObject());
std::auto_ptr p2 = p1; // Copy and transfer ownership. 
                                 // p1 gets set to empty!
p2->DoSomething(); // Works.
p1->DoSomething(); // Oh oh. Hopefully raises some NULL pointer exception.

ANTIGUA RESPUESTA

Un puntero inteligente es una clase que envuelve un puntero de C ++ ‘sin formato’ (o ‘desnudo’), para administrar la vida útil del objeto al que se apunta. No existe un único tipo de puntero inteligente, pero todos intentan abstraer un puntero sin formato de una manera práctica.

Deben preferirse los punteros inteligentes a los punteros sin formato. Si siente que necesita usar punteros (primero considere si De Verdad hacer), normalmente querrá usar un puntero inteligente, ya que esto puede aliviar muchos de los problemas con los punteros en bruto, principalmente olvidando eliminar el objeto y la pérdida de memoria.

Con punteros sin formato, el programador tiene que destruir explícitamente el objeto cuando ya no sea útil.

// Need to create the object to achieve some goal
MyObject* ptr = new MyObject(); 
ptr->DoSomething(); // Use the object in some way
delete ptr; // Destroy the object. Done with it.
// Wait, what if DoSomething() raises an exception...?

En comparación, un puntero inteligente define una política sobre cuándo se destruye el objeto. Todavía tienes que crear el objeto, pero ya no tienes que preocuparte por destruirlo.

SomeSmartPtr ptr(new MyObject());
ptr->DoSomething(); // Use the object in some way.

// Destruction of the object happens, depending 
// on the policy the smart pointer class uses.

// Destruction would happen even if DoSomething() 
// raises an exception

La política más simple en uso implica el alcance del objeto contenedor de puntero inteligente, como implementado por boost::scoped_ptr o std::unique_ptr.

void f()

    
       std::unique_ptr ptr(new MyObject());
       ptr->DoSomethingUseful();
     // ptr goes out of scope -- 
      // the MyObject is automatically destroyed.

    // ptr->Oops(); // Compile error: "ptr" not defined
                    // since it is no longer in scope.

Tenga en cuenta que std::unique_ptr las instancias no se pueden copiar. Esto evita que el puntero se elimine varias veces (incorrectamente). Sin embargo, puede pasar referencias a otras funciones que llame.

std::unique_ptrLos s son útiles cuando desea vincular la vida útil del objeto a un bloque de código en particular, o si lo incrustó como datos de miembros dentro de otro objeto, la vida útil de ese otro objeto. El objeto existe hasta que se sale del bloque de código que lo contiene, o hasta que el objeto que lo contiene se destruye.

Una política de puntero inteligente más compleja implica el recuento de referencias del puntero. Esto permite copiar el puntero. Cuando se destruye la última “referencia” al objeto, el objeto se elimina. Esta política es implementada por boost::shared_ptr y std::shared_ptr.

void f()

    typedef std::shared_ptr MyObjectPtr; // nice short alias
    MyObjectPtr p1; // Empty

    
        MyObjectPtr p2(new MyObject());
        // There is now one "reference" to the created object
        p1 = p2; // Copy the pointer.
        // There are now two references to the object.
     // p2 is destroyed, leaving one reference to the object.
 // p1 is destroyed, leaving a reference count of zero. 
  // The object is deleted.

Los punteros contados de referencia son muy útiles cuando la vida útil de su objeto es mucho más complicada y no está vinculada directamente a una sección particular de código oa otro objeto.

Hay un inconveniente de hacer referencia a punteros contados: la posibilidad de crear una referencia colgante:

// Create the smart pointer on the heap
MyObjectPtr* pp = new MyObjectPtr(new MyObject())
// Hmm, we forgot to destroy the smart pointer,
// because of that, the object is never destroyed!

Otra posibilidad es crear referencias circulares:

struct Owner 
   std::shared_ptr other;
;

std::shared_ptr p1 (new Owner());
std::shared_ptr p2 (new Owner());
p1->other = p2; // p1 references p2
p2->other = p1; // p2 references p1

// Oops, the reference count of of p1 and p2 never goes to zero!
// The objects are never destroyed!

Para solucionar este problema, tanto Boost como C ++ 11 han definido un weak_ptr para definir una referencia débil (no contada) a un shared_ptr.

Aquí hay una respuesta simple para estos días de C ++ moderno (C ++ 11 y posteriores):

  • “¿Qué es un puntero inteligente?”

    Es un tipo cuyos valores se pueden usar como punteros, pero que proporciona la característica adicional de administración automática de memoria: cuando un puntero inteligente ya no está en uso, la memoria a la que apunta se desasigna (ver también la definición más detallada en Wikipedia).

  • “¿Cuándo debo usar uno?”

    En código que implica rastrear la propiedad de una parte de la memoria, asignar o desasignar; el puntero inteligente a menudo le ahorra la necesidad de hacer estas cosas explícitamente.

  • “¿Pero qué puntero inteligente debo usar en cuál de esos casos?”
    • Usar std::unique_ptr cuando desea que su objeto viva tanto como viva una única referencia de propiedad. Por ejemplo, utilícelo para un puntero a la memoria que se asigna al ingresar a algún alcance y se desasigna al salir del alcance.
    • Usar std::shared_ptr cuando quiera hacer referencia a su objeto desde varios lugares, y no quiera que su objeto sea desasignado hasta que todas estas referencias desaparezcan.
    • Usar std::weak_ptr cuando desee hacer referencia a su objeto desde varios lugares, para aquellas referencias para las que está bien ignorar y desasignar (por lo que solo notarán que el objeto se ha ido cuando intente eliminar la referencia).
    • No use el boost:: punteros inteligentes o std::auto_ptr excepto en casos especiales sobre los que puede leer si es necesario.
  • “¡Oye, no pregunté cuál usar!”

    Ah, pero realmente querías, admítelo.

  • “Entonces, ¿cuándo debería usar punteros regulares?”

    Principalmente en código ajeno a la propiedad de la memoria. Normalmente, esto sería en funciones que obtienen un puntero de otro lugar y no asignan ni desasignan, y no almacenan una copia del puntero que dura más que su ejecución.

Un puntero inteligente es un tipo similar a un puntero con alguna funcionalidad adicional, por ejemplo, desasignación automática de memoria, recuento de referencias, etc.

Una pequeña introducción está disponible en la página Smart Pointers – What, Why, Which ?.

Uno de los tipos simples de puntero inteligente es std::auto_ptr (capítulo 20.4.5 del estándar C ++), que permite desasignar la memoria automáticamente cuando está fuera de alcance y que es más robusto que el simple uso de punteros cuando se lanzan excepciones, aunque menos flexible.

Otro tipo conveniente es boost::shared_ptr que implementa el recuento de referencias y desasigna automáticamente la memoria cuando no quedan referencias al objeto. Esto ayuda a evitar pérdidas de memoria y es fácil de usar para implementar RAII.

El tema se trata en profundidad en el libro. “Plantillas C ++: La guía completa” por David Vandevoorde, Nicolai M. Josuttis, capítulo Capítulo 20. Punteros inteligentes. Algunos temas cubiertos:

  • Protección contra excepciones
  • Titulares, (nota, std :: auto_ptr es la implementación de este tipo de puntero inteligente)
  • La adquisición de recursos es inicialización (esto se usa con frecuencia para la administración de recursos segura para excepciones en C ++)
  • Limitaciones del titular
  • Recuento de referencias
  • Acceso concurrente al contador
  • Destrucción y desasignación

Te mostramos las comentarios y valoraciones de los usuarios

Recuerda que puedes comunicar esta noticia si lograste el éxito.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *