smart punteros pointer inteligentes c++ boost smart-pointers

punteros - smart pointer c++



la mejor práctica al devolver punteros inteligentes (9)

Depende de cuál sea el significado del puntero.

Al devolver un shared_pointer, está sintácticamente diciendo "Compartirá la propiedad de este objeto", de modo que, si el objeto contenedor original muere antes de liberar su puntero, ese objeto seguirá existiendo.

Al devolver un puntero sin formato, dice: "Conoces este objeto, pero no lo posees". Es una forma de pasar el control, pero no de mantener la vida ligada al propietario original.

(en algunos c-programs más antiguos, significa "ahora es tu problema eliminarme", pero recomiendo evitar esto)

Normalmente, el valor predeterminado para compartir me ahorra muchas molestias, pero depende de tu diseño.

¿Cuál es la mejor práctica al devolver un puntero inteligente, por ejemplo, un impulso :: shared_ptr? ¿Debo devolver de forma estándar el puntero inteligente o el puntero sin procesar subyacente? Vengo de C #, por lo que siempre devuelvo punteros inteligentes porque me parece correcto. Me gusta esto (omitiendo const-correctness para código más corto):

class X { public: boost::shared_ptr<Y> getInternal() {return m_internal;} private: boost::shared_ptr<Y> m_internal; }

Sin embargo, he visto a algunos codificadores experimentados devolver el puntero sin formato y poner los punteros sin procesar en vectores. ¿Cuál es la forma correcta de hacerlo?


En mi opinión, en C ++, siempre debe justificar el uso de un puntero sin protección.

Puede haber muchas razones válidas: una necesidad de muy alto rendimiento, de uso de memoria muy bajo, para tratar con bibliotecas heredadas, debido a algún problema con la estructura de datos subyacente que está almacenando el puntero. Pero los punteros [asignados dinámicamente] son ​​un tanto "malvados", ya que tiene que desasignar la memoria en cada ruta de ejecución posible y casi seguramente olvidará una.


No hay una manera "correcta". Realmente depende del contexto.

Puede manejar internamente la memoria con un puntero inteligente y externamente proporcionar referencias o punteros sin procesar. Después de todo, el usuario de su interfaz no necesita saber cómo administra la memoria internamente. En un contexto sincrónico, esto es seguro y eficiente. En un contexto asincrónico, hay muchas trampas.

Si no está seguro de qué hacer, puede devolver de forma segura punteros inteligentes a su interlocutor. El objeto será desasignado cuando el recuento de referencias llegue a cero. Solo asegúrate de no tener una clase que mantenga punteros inteligentes de objetos para siempre, evitando así la desasignación cuando sea necesario.

Como última nota, en C ++ no use en exceso los objetos asignados dinámicamente. Hay muchos casos en los que no necesita un puntero y puede trabajar en referencias y referencias de referencias. Eso es más seguro y reduce la presión sobre el asignador de memoria.


No pondría punteros crudos en vectores.

En caso de que utilicen auto_ptr o boost :: scoped_ptr, no pueden usar (o devolver) nada más que punteros sin procesar. Eso podría explicar su forma de codificación, supongo.


Normalmente devuelvo punteros inteligentes "propios" / "únicos" de fábricas o similares para aclarar quién es el responsable de la limpieza.

Este ejemplo https://ideone.com/qJnzva muestra cómo devolver un std::unique_ptr que se eliminará cuando el alcance de la variable a la que el llamador asigna el valor quede fuera del alcance.

Si bien es cierto que el puntero inteligente borra su propio puntero, el tiempo de vida de la variable que contiene el puntero inteligente es 100% controlado por la persona que llama, por lo que la persona que llama decide cuándo se borra el puntero. Sin embargo, dado que es un puntero inteligente "exclusivo" y "propietario", ningún otro cliente puede controlar la duración.


Nunca devolvería un puntero sin formato, en su lugar devolvería un weak_ptr para decirle al usuario del puntero que no tiene el control sobre el recurso.

Si devuelve un weak_ptr es muy poco probable que haya punteros colgantes en la aplicación.

Si hay un problema de rendimiento, devolvería una referencia al objeto y un método hasValidXObject.


Sigo las siguientes pautas para pasar argumentos de punteros a funciones y devolver punteros:

boost::shared_ptr

La API y el cliente comparten la propiedad de este objeto. Sin embargo, debe tener cuidado para evitar referencias circulares con shared_ptr , si los objetos representan algún tipo de gráfico. Intento limitar mi uso de shared_ptr por este motivo.

boost::weak_ptr / raw pointer

API posee este objeto, se le permite compartirlo mientras sea válido. Si existe la posibilidad de que el cliente viva más tiempo que la API, uso un weak_ptr.

std::auto_ptr

API está creando un objeto pero el cliente posee el objeto. Esto asegura que el código de retorno es excepcionalmente seguro y establece claramente que la propiedad se transfiere.

boost::scoped_ptr

Para punteros a objetos almacenados en la pila o como variables de miembro de clase. Intento usar scoped_ptr primero.

Al igual que todas las pautas, habrá momentos en que las reglas entren en conflicto o tengan que ser dobladas, entonces trato de usar la inteligencia.


const boost :: shared_ptr & getInternal () {return m_internal;}

Esto evita una copia.

Algunas veces le gustaría devolver una referencia, por ejemplo:

  • Y & operator * () {return * m_internal; }
  • const Y & operator * () const {return * m_internal; }

Esto también es bueno si la referencia será utilizada y descartada inmediatamente. Lo mismo ocurre con un puntero sin formato. Devolver un weak_ptr también es una opción.

Los 4 son buenos dependiendo de los objetivos. Esta pregunta requiere una discusión más extensa.


depende de tus objetivos

devolver ciegamente los datos internos a los datos internos puede no ser una buena idea (que es muy sensible a la tarea que intentas resolver); es mejor que solo ofrezcas algunos doX () y doY () que usan el puntero internamente .

por otro lado, si devuelve el ptr inteligente, también debe considerar que no creará referencias circulares mutuas cuando los objetos terminen sin poder destruirse entre sí (weak_ptr podría ser una mejor opción en ese caso).

de lo contrario, como ya se mencionó anteriormente, las consideraciones de rendimiento / código heredado / duración deben tenerse en cuenta.