2008-08-26 18 views
8

He aprendido en la universidad que siempre tiene que liberar los objetos que no utiliza, pero no cómo lo hace realmente. Por ejemplo, estructurar su código correctamente, etc. ¿Hay alguna regla general sobre cómo manejar los punteros en C++?C++ Gestión de la memoria

Actualmente no estoy autorizado a utilizar boost. Tengo que apegarme a C++ puro porque el framework que estoy usando prohíbe cualquier uso de genéricos.

+0

Lástima que esto es tan viejo. Me encantaría escuchar una explicación sobre por qué no puedes usar (específicamente) genéricos. – jmucchiello

+0

De la documentación del marco: No use plantillas. No son portátiles para diferentes sistemas operativos, especialmente en la forma en que son compatibles con compiladores y editores de enlaces. Eso es todo lo que puedo decir al respecto –

+0

Las plantillas fueron soportadas muy bien en los diferentes sistemas operativos en 2009, siempre y cuando dichos sistemas operativos tengan compiladores apenas competentes disponibles para ellos. Cualquiera que escriba un marco debería haber podido encontrar alguna evidencia real para su reclamo. –

Respuesta

14

He trabajado con el sistema operativo Symbian integrado, que tenía un sistema excelente para esto, basado enteramente en las convenciones del desarrollador.

  1. Solo un objeto tendrá un puntero. Por defecto, este es el creador.
  2. La propiedad se puede transmitir. Para indicar el paso de la propiedad, el objeto se pasa como un puntero en la firma del método (por ejemplo, void Foo (Bar * zonk);).
  3. El propietario decidirá cuándo eliminar el objeto.
  4. Para pasar un objeto a un método solo para su uso, el objeto se pasa como referencia en la firma del método (por ejemplo, void Foo (Bat & zonk);).
  5. Las clases no propietarias pueden almacenar referencias (nunca punteros) a los objetos que se les da solo cuando pueden estar seguros de que el propietario no lo destruirá durante el uso.

Básicamente, si una clase simplemente usa algo, utiliza una referencia. Si una clase posee algo, usa un puntero.

Esto funcionó muy bien y fue un placer usarlo. Los problemas de memoria eran muy raros.

+0

Usaría std :: auto_ptr <> para mostrar que se está transfiriendo el barco propietario (en lugar del puntero RAW). Presumiblemente, ya está almacenando en un puntero inteligente, si se espera que la propiedad exclusiva std :: auto_ptr <> sea perfecta. –

+0

El OP dijo que no podía usar genéricos, lo que presumiblemente descarta cualquier tipo de puntero inteligente. –

+0

¿No se descarga el volcado de la aplicación si el objeto apuntado por la referencia se elimina por error? Si hubiera sido un puntero, podríamos verificar null. (Siempre que el eliminador lo establezca como nulo) – balki

5

Reglas:

  1. Siempre que sea posible, utilice un smart pointer. Boost tiene algunos good ones.
  2. Si no puede usar un puntero inteligente, null out your pointer after deleting it.
  3. Nunca trabaje en cualquier lugar que no le permitirá utilizar la regla 1.

Si alguien rechaza la regla 1, recuerde que si usted agarra el código de otra persona, cambie los nombres de las variables y eliminar los avisos de copyright, nadie alguna vez lo notarás A menos que sea un proyecto escolar, donde realmente verifican ese tipo de travesuras con herramientas bastante sofisticadas. Ver también, this question.

+0

-1 para anular. No lo discutiré aquí, pero es ** no ** una buena práctica. Si el código se escribe bastante bien, no debería ser necesario, ya que no debe eliminar antes de que esté seguro de que ya no existen referencias ** **/punteros a su bloque de memoria asignada. –

0
  • Cuando usted tiene que utilizar gestionar la memoria manualmente, asegúrese de llamar a eliminar en la misma alcance/función/clase/módulo, que siempre se aplica en primer lugar, por ejemplo:
  • permitir a la visita de un función asignar la memoria que está llena, no devuelva punteros new'ed.
  • Siempre llame a eliminar en el mismo archivo .exe/dll que llamó new in, porque de lo contrario puede tener problemas con corrupciones de montón (diferentes bibliotecas de tiempo de ejecución incompatibles).
2

En el caso general (administración de recursos, donde el recurso no es necesariamente memoria), necesita estar familiarizado con el RAII pattern.Esta es una de las piezas de información más importantes para los desarrolladores de C++.

0

que podría derivar todo de alguna clase base que implementar puntero inteligente como la funcionalidad (utilizando ref)/unref() métodos (y un contador.

Todos los puntos destacados por @Timbo son importantes en el diseño de esa clase base.

1

día G,

me gustaría sugerir la lectura de las secciones pertinentes de "C++ eficaz" por Scott Meyers. Fácil de leer y que cubre algunos aspectos críticos interesantes para atrapar a los incautos.

estoy también intrigado por la falta de plantillas. Entonces no hay STL o Boost. Guau.

BTW Lograr que las personas acuerden convenciones es una excelente idea. Al igual que lograr que todos acuerden convenciones para OOD. Por cierto, la última edición de Effective C++ no tiene el excelente capítulo sobre las convenciones de OOD que tuvo la primera edición, lo que es una lástima, p. convenciones como herencia virtual pública siempre modela una relación "isa".

Rob

3

me gustaría añadir otra regla aquí:

  • ¿No nueva/eliminar un objeto cuando un objeto automático hará muy bien.

Hemos encontrado que los programadores que son nuevos en C++, o los programadores provenientes de lenguajes como Java, parecen aprender cosas nuevas y luego usarlas obsesivamente cuando quieren crear cualquier objeto, independientemente del contexto. Esto es especialmente pernicioso cuando un objeto se crea localmente dentro de una función simplemente para hacer algo útil. El uso de nuevo de esta manera puede ser perjudicial para el rendimiento y puede hacer que sea demasiado fácil introducir fugas de memoria tontas cuando se olvida la eliminación correspondiente. Sí, los punteros inteligentes pueden ayudar con este último, pero no resolverán los problemas de rendimiento (suponiendo que se utiliza nuevo/eliminar o un equivalente detrás de escena). Curiosamente (bueno, tal vez), hemos encontrado que eliminar a menudo tiende a ser más caro que nuevo cuando se usa Visual C++.

Parte de esta confusión también proviene del hecho de que las funciones que llaman pueden tomar punteros, o incluso punteros inteligentes, como argumentos (cuando las referencias quizás sean mejores/claras). Esto les hace pensar que necesitan "crear" un puntero (mucha gente parece pensar que esto es lo que hace nuevo) para poder pasar un puntero a una función. Claramente, esto requiere algunas reglas sobre cómo se escriben las API para hacer que las convenciones de llamadas sean lo menos ambiguas posible, lo cual se refuerza con comentarios claros provistos con el prototipo de la función.

+0

Recogiendo desde Nice" Esto les hace pensar que necesitan para "crear" un puntero (mucha gente parece pensar que esto es lo que hace nuevo) para poder pasar un puntero a una función.) "Tal vez, pero creo que es porque la mayoría de las universidades enseñan Java primero. En Java tienes que" volver a empezar "todo y es difícil romper el hábito, especialmente cuando los dos idiomas se ven muy similares, pero de hecho son completamente diferentes. ¡Diversión, intente trabajar con ambos al mismo tiempo! –

2

En general, evite asignar desde el montón a menos que sea necesario. Si es necesario, utilice el recuento de referencias para objetos que son de larga duración y deben compartirse entre diversas partes de su código.

A veces es necesario asignar objetos dinámicamente, pero solo se usarán dentro de un lapso de tiempo determinado. Por ejemplo, en un proyecto anterior, necesité crear una representación compleja en la memoria de un esquema de base de datos, básicamente un complejo gráfico cíclico de objetos. Sin embargo, el gráfico solo era necesario para la duración de una conexión a la base de datos, después de lo cual todos los nodos podrían liberarse en una sola toma. En este tipo de escenario, un buen patrón para usar es algo que llamo el "idioma local de GC". No estoy seguro de si tiene un nombre "oficial", ya que es algo que solo he visto en mi propio código, y en Cocoa (ver NSAutoreleasePool en la referencia de Cocoa de Apple).

En pocas palabras, crea un objeto "colector" que mantiene los punteros a los objetos temporales que asigna con el nuevo. Por lo general, está vinculado a algún ámbito en su programa, ya sea un alcance estático (por ejemplo, como un objeto asignado a la pila que implementa el modismo RAII) o uno dinámico (por ejemplo, relacionado con la vida de una conexión de base de datos, como mi proyecto anterior). Cuando se libera el objeto "colector", su destructor libera todos los objetos a los que apunta.

Además, al igual que DrPizza, creo que la restricción de no usar plantillas es demasiado dura. Sin embargo, después de haber hecho un gran desarrollo en las versiones antiguas de Solaris, AIX y HP-UX (hace poco, sí, estas plataformas todavía están vivas en Fortune 50), puedo decirles que si realmente les importa la portabilidad, debería usar plantillas lo menos posible. Sin embargo, usarlos para contenedores y punteros inteligentes debería estar bien (me funcionó). Sin plantillas, la técnica que describí es más dolorosa de implementar. Se requeriría que todos los objetos gestionados por el "recopilador" deriven de una clase base común.

+0

Si bien un idioma local de GC suena bien, no es necesario. La administración manual de la memoria no es tan difícil de hacer bien. –

Cuestiones relacionadas