2009-12-15 42 views
20

Me enteré de que el puntero inteligente se utiliza para la gestión de recursos y es compatible con RAII.pros y contras de punteros inteligentes

Pero, ¿cuáles son los casos de esquina en que el puntero inteligente no parece inteligente y hay que tenerlo en cuenta al usarlo?

+0

Ya estoy, no has visto la serie MI de la mía. – Ashish

Respuesta

18

punteros inteligentes no ayudan contra bucles en las estructuras gráfico similar.

Por ejemplo, el objeto A mantiene un puntero inteligente al objeto B y el objeto B al objeto A. Si suelta todos los punteros a A y B antes de la desconexión A de B (o B de A) B se abrazarán y formarán una feliz pérdida de memoria.

La recolección de elementos no utilizados podría ayudar a eso, podría ver que ambos objetos son inalcanzables y liberarlos.

+16

* Los punteros inteligentes * fuertes en los bucles causarán fugas de memoria, pero el uso cuidadoso de los punteros * weak * (una parte de cualquier biblioteca de puntero inteligente completa) le permite utilizar punteros inteligentes sin fugas, incluso con estructuras circulares. –

+1

esto también está relacionado con una fuga común con GC, cuando tiene una estructura circular y ya no necesita parte de ella, tiene que romper manualmente el ciclo para permitir GC (o permitir que GC recolecte toda la estructura) p. C# eventos –

+3

@jk: No es exactamente lo mismo. Un GC puede manejar el caso en el que A señala a B y B apunta a A suponiendo que ningún otro objeto apunta a ninguno de los objetos. Solo tiene que "romper el ciclo" si todavía hay otra referencia a A o B. Por lo tanto, realmente no es el ciclo el problema. Un GC medio decente puede manejarlos. El problema es cuando crea referencias de datos raramente utilizados a datos de larga duración, sin darse cuenta (como en los eventos). No tiene nada que ver con los ciclos. – jalf

1

Estas son algunas cosas

  • hay ninguna entidad definida que destruye el objeto. A menudo se desea saber exactamente cuándo y cómo se destruye un objeto
  • Dependencias circulares: si existen, tendrá una pérdida de memoria. Eso es generalmente donde weak_ptr viene al rescate.
+1

Por lo general, es suficiente saber que un objeto fue destruido, más o menos en el momento apropiado. No me he encontrado con muchos casos en los que necesitaba saber exactamente cuándo y en respuesta a qué. –

+2

Se garantiza que la destrucción del objeto tendrá lugar en un lugar bien definido (incluso en el caso de los punteros inteligentes ref-contados, la destrucción se garantiza que ocurra en un lugar bien definido; es solo eso con punteros inteligentes ref-contados no conoces ese lugar hasta el tiempo de ejecución). –

+0

"_Eso suele ser donde weak_ptr viene al rescate." Usualmente no, 'weak_ptr' solo puede ocultar el problema. – curiousguy

6

Esto es bastante interesante: Smart Pointers.
Es un capítulo de muestra del "Diseño moderno en C++" de Andrei Alexandrescu.

0

Raymond Chen es notoriamente ambivalente acerca de los indicadores inteligentes. Existen problemas sobre when the destructor actually runs (tenga en cuenta que el destructor se ejecuta en un momento bien definido en un orden bien definido, es solo que de vez en cuando se olvidará que es después de la última línea en su función).

Recuerde también que "puntero inteligente" es una categoría bastante grande. Incluyo std::vector en esa categoría (a std::vector is essentially a smart array).

+4

No, std :: vector no es una matriz inteligente. ¡copiar un vector duplica la matriz! –

+1

administra su propia memoria aunque –

+6

Pero algo que "maneja su propia memoria es solo una" clase de C++ "y * nada * más.Es lo mínimo que debe esperar de * cualquier * clase. Un puntero inteligente es aquel que administra la memoria * de otras personas *. Uno al que se le da un puntero y se encarga de eliminarlo en el momento correcto. Y lo del destructor no tiene nada que ver con los indicadores inteligentes. Es algo general en C++, y cualquier programador de C++ debe saberlo de memoria. – jalf

3

Además de las limitaciones técnicas (ya mencionadas: dependencias circulares), yo diría que lo más importante acerca de los punteros inteligentes es recordar que todavía es una solución para eliminar los objetos asignados en el montón eliminados. La asignación de pila es la mejor opción para la mayoría de los casos, junto con el uso de referencias, para administrar la vida útil de los objetos.

+0

La asignación de la pila ciertamente no es la mejor opción en la mayoría de los casos. La asignación de la pila tiene su propia limitación y usted puede alcanzarla rápidamente si maneja una cantidad razonable de estructuras de datos. La mayoría de los programas complejos usan la asignación de montón simplemente porque arruinas la pila bastante rápido cuando los números crecen. El valor predeterminado de Linux es 8megs pr thread IIRC. En la mayoría de los sistemas integrados, está en el rango de kb. –

+0

Veo tu punto. Pero, ¿no preferirías crear clases específicas de contenedores que asignen y desasignen sus propios trozos de memoria en lugar de asignar bloques de memoria a todos lados en tu código y esperar que no hayas olvidado a nadie? De esta manera, puede eludir el problema del tamaño de la pila ... –

+0

Claro que estamos de acuerdo - eso es lo bueno de C++ en mi opinión :) Estaba argumentando contra su punto de que "la asignación de la pila es la mejor opción para la mayoría casos". –

10

Me gustaría mencionar las limitaciones de rendimiento. Los punteros inteligentes generalmente usan operaciones atómicas (como InterlockedIncrement en Win32 API) para el recuento de referencias. Estas funciones son significativamente más lentas que la aritmética de enteros simples.

En la mayoría de los casos, esta penalización de rendimiento no es un problema, solo asegúrese de no hacer demasiadas copias innecesarias del objeto del puntero inteligente (prefiera pasar el puntero inteligente por referencia en las llamadas a funciones).

+1

A menos que la penalización de rendimiento se vuelva obvia, desaconsejaría usar referencias a punteros inteligentes. Cuando se usa como argumentos de funciones, no tiene un impacto negativo, pero puede ser utilizado como miembros de la clase. ¡Cuidado! –

+1

@Benoit: estoy bastante seguro de que "pasar el puntero inteligente por referencia" significa específicamente en llamadas, en cuyo caso no se debe aconsejar a alguien que lo pase por valor. –

+2

Sí, me refería a pasarlo como argumento a una función. He editado la respuesta para que quede más claro. –

0

Existe un problema con el recuento de referencias en ciertos tipos de estructuras de datos que tienen ciclos. También puede haber problemas para acceder a punteros inteligentes desde múltiples subprocesos, el acceso concurrente a recuentos de referencia puede causar problemas. Hay una utilidad en boost llamada atomic.hpp que puede mitigar este problema.

0

Muchas personas tienen problemas al usar punteros inteligentes combinados con punteros sin procesar (para los mismos objetos). Un ejemplo típico es cuando se interactúa con una API que utiliza punteros sin formato.
Por ejemplo; en boost::shared_ptr hay una función .get() que devuelve el puntero sin formato. Buena funcionalidad si se usa con cuidado, pero muchas personas parecen tropezar con ella.
En mi humilde opinión es un ejemplo de "abstracción con fugas".

5

Tenga cuidado en las transiciones - al asignar punteros crudos e inteligentes. Los malos indicadores inteligentes, como _com_ptr_t, lo empeoran al permitir conversiones implícitas. La mayoría de los errores ocurren en la transición.

Tenga cuidado con los ciclos - como se mencionó, necesita punteros débiles para romper los ciclos. Sin embargo, en un gráfico complejo que no siempre es fácil de hacer.

Demasiada elección - la mayoría de las bibliotecas ofrecen implementaciones diferentes con diferentes ventajas/inconvenientes. Desafortunadamente, la mayoría de las veces estas diferentes variantes no son compatibles, lo que se convierte en una prueba al mezclar bibliotecas. (por ejemplo, LibA usa LOKI, LibB usa refuerzo). Tener que planificar por adelantado para enable_shared_from_this es una mierda, tener que decidir convenciones de nomenclatura entre intrusive_ptr, shared_ptr y weak_ptr para un grupo de objetos apesta.


Para mí, la ventaja más correo de shared_ptr (o uno de una funcionalidad similar) es que está acoplado a su política de destrucción en la creación. Tanto C++ como Win32 ofrecen tantas maneras de deshacerse de cosas que ni siquiera es divertido. El acoplamiento en tiempo de construcción (sin afectar el tipo real del puntero) significa que tengo ambas políticas en un solo lugar.