Pregunta

En la universidad aprendí que siempre tienes que liberar los objetos no utilizados, pero no cómo lo haces realmente.Por ejemplo, estructurar correctamente su código, etc.¿Existen reglas generales sobre cómo manejar punteros en C++?

Actualmente no puedo usar boost.Tengo que ceñirme a C++ puro porque el marco que estoy usando prohíbe el uso de genéricos.

¿Fue útil?

Solución

He trabajado con el sistema operativo Symbian integrado, que tenía un excelente sistema para esto, basado completamente en convenciones de desarrolladores.

  1. Sólo un objeto poseerá un puntero.Por defecto este es el creador.
  2. La propiedad se puede transmitir.Para indicar la transferencia de propiedad, el objeto se pasa como un puntero en la firma del método (p. ej.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 (p. ej.void Foo(Bat &zonk);).
  5. Las clases que no son propietarios pueden almacenar referencias (nunca punteros) a objetos que se les proporcionan sólo cuando pueden estar seguros de que el propietario no los destruirá durante su uso.

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

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

Otros consejos

Normas:

  1. Siempre que sea posible, utilice unpuntero inteligente.Boost tiene algunosBuenos.
  2. Si no puede usar un puntero inteligente, anule su puntero después de eliminarlo.
  3. Nunca trabajes en ningún lugar que no te permita usar la regla 1.

Si alguien no permite la regla 1, recuerde que si toma el código de otra persona, cambia los nombres de las variables y elimina los avisos de derechos de autor, nadie se dará cuenta.A menos que sea un proyecto escolar, donde realmente verifican ese tipo de travesuras con herramientas bastante sofisticadas.Ver también, esta pregunta.

Yo agregaría otra regla aquí:

  • No cree/elimine un objeto cuando un objeto automático funcionará bien.

Hemos descubierto que los programadores que son nuevos en C++, o los programadores que vienen de lenguajes como Java, parecen aprender algo nuevo y luego usarlo obsesivamente cuando quieren crear cualquier objeto, independientemente del contexto.Esto es especialmente pernicioso cuando un objeto se crea localmente dentro de una función únicamente para hacer algo útil.Usar new de esta manera puede ser perjudicial para el rendimiento y puede hacer que sea muy fácil introducir pérdidas de memoria tontas cuando se olvida la eliminación correspondiente.Sí, los punteros inteligentes pueden ayudar con esto último, pero no resolverán los problemas de rendimiento (suponiendo que se utilice nuevo/eliminar o un equivalente detrás de escena).Curiosamente (bueno, tal vez), hemos descubierto que eliminar a menudo tiende a ser más costoso que nuevo cuando se usa Visual C++.

Parte de esta confusión también proviene del hecho de que las funciones a las que llaman pueden tomar punteros, o incluso punteros inteligentes, como argumentos (cuando las referencias tal vez serían mejores/más claras).Esto les hace pensar que necesitan "crear" un puntero (mucha gente parece pensar que esto es lo que hace new) para poder pasar un puntero a una función.Claramente, esto requiere algunas reglas sobre cómo se escriben las API para que las convenciones de llamada sean lo más inequívocas posible, que se refuerzan con comentarios claros proporcionados con el prototipo de función.

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

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

A veces es necesario asignar objetos dinámicamente, pero solo se utilizarán dentro de un cierto período de tiempo.Por ejemplo, en un proyecto anterior necesitaba crear una representación compleja en memoria de un esquema de base de datos, básicamente un gráfico cíclico complejo de objetos.Sin embargo, el gráfico solo era necesario mientras duraba la conexión a la base de datos, después de lo cual todos los nodos podían liberarse de una sola vez.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 cacao (ver NSAutoreleasePool en la referencia de Apple's Cocoa).

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

Además, al igual que DrPizza, creo que la restricción de no utilizar plantillas es demasiado estricta.Sin embargo, después de haber desarrollado mucho en versiones antiguas de Solaris, AIX y HP-UX (recientemente, sí, estas plataformas todavía están vivas en Fortune 50), puedo decirle que si realmente le importa la portabilidad, Debe utilizar plantillas lo menos posible.Sin embargo, usarlos para contenedores y punteros inteligentes debería estar bien (funcionó para mí).Sin plantillas, la técnica que describí es más complicada de implementar.Requeriría que todos los objetos gestionados por el "recolector" derivaran de una clase base común.

Buen día,

Sugeriría leer las secciones relevantes de "Effective C++" de Scott Meyers.Es fácil de leer y cubre algunos trucos interesantes para atrapar a los incautos.

También me intriga la falta de plantillas.Entonces no hay STL ni Boost.Guau.

Por cierto, lograr que la gente se ponga de acuerdo sobre las convenciones es una idea excelente.Al igual que lograr que todos se pongan de acuerdo sobre las convenciones para OOD.Por cierto, la última edición de Effective C++ no tiene el excelente capítulo sobre convenciones OOD que tenía la primera edición, lo cual es una lástima, p.convenciones como la herencia virtual pública siempre modela una relación "isa".

Robar

  • Cuando tenga que usar Manay Memory manualmente, asegúrese de llamar a Eliminar en el mismo alcance/función/clase/módulo, lo que se aplica primero, por ejemplo:
  • Deje que la persona que llame de una función asigne la memoria llena por ella, no devuelva los punteros nuevos.
  • Siempre llame a eliminar en el mismo exe/dll en el que llamó a nuevo, porque de lo contrario puede tener problemas con daños en el montón (diferentes bibliotecas de tiempo de ejecución incompatibles).

podría derivar todo de alguna clase base que implemente una funcionalidad similar a un puntero inteligente (usando métodos ref()/unref() y un contador.

Todos los puntos resaltados por @Timbo son importantes al diseñar esa clase base.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top