Estoy trabajando en una aplicación mediana incrustada en C usando técnicas similares a OO. Mis "clases" son módulos .h / .c que usan estructuras de datos y estructuras de punteros de función para emular la encapsulación, el polimorfismo y la inyección de dependencia.
Ahora, uno esperaría que una myModule_create(void)
función venga con una myModule_destroy(pointer)
contraparte. Pero el proyecto está incrustado, los recursos que se instancian de manera realista nunca deben ser lanzados.
Quiero decir, si tengo 4 puertos seriales UART y creo 4 instancias UART con sus pines y configuraciones requeridas, no hay absolutamente ninguna razón para querer destruir UART # 2 en algún momento durante el tiempo de ejecución.
Entonces, siguiendo el principio YAGNI (no lo vas a necesitar), ¿debería omitir los destructores? Esto me parece extremadamente extraño, pero no puedo pensar en un uso para ellos; los recursos se liberan cuando el dispositivo se apaga.
fuente
myModule_create(void)
función? Simplemente puede codificar las instancias específicas que espera utilizar en la interfaz que expone.Respuestas:
Glampert tiene razón; No hay necesidad de destructores aquí. Simplemente crearían una acumulación de código y una trampa para los usuarios (usar un objeto después de que se llama a su destructor es un comportamiento indefinido).
Sin embargo, debe estar seguro de que realmente no hay necesidad de deshacerse de los objetos. Por ejemplo, ¿necesita tener un objeto para un UART que no esté actualmente en uso?
fuente
La forma más fácil que he encontrado para detectar pérdidas de memoria es poder salir limpiamente de su aplicación. Muchos compiladores / entornos proporcionan una forma de verificar la memoria que aún está asignada cuando su aplicación sale. Si no se proporciona uno, generalmente hay una manera de agregar algo de código justo antes de salir que puede resolverlo.
Entonces, ciertamente proporcionaría constructores, destructores y lógica de apagado incluso en un sistema embebido que "teóricamente" nunca debería salir para facilitar la detección de pérdidas de memoria por sí sola. En realidad, la detección de pérdida de memoria es aún más importante si el código de la aplicación nunca debe salir.
fuente
En mi desarrollo, que hace un uso extensivo de tipos de datos opacos para fomentar un enfoque tipo OO, yo también luché con esta pregunta. Al principio, decididamente estaba en el campo de eliminar el destructor desde la perspectiva de YAGNI, así como la perspectiva del "código muerto" de MISRA. (Tenía mucho espacio de recursos, eso no era una consideración).
Sin embargo ... la falta de un destructor puede dificultar las pruebas, como en las pruebas automatizadas de unidad / integración. Convencionalmente, cada prueba debe admitir una configuración / desmontaje para que los objetos puedan crearse, manipularse y luego destruirse. Se destruyen para asegurar un punto de partida limpio y sin contaminación para la siguiente prueba. Para hacer esto, la clase necesita un destructor.
Por lo tanto, en mi experiencia, el "aint't" en YAGNI resulta ser un "are" y terminé creando destructores para cada clase, ya sea que pensara que lo necesitaba o no. Incluso si omití las pruebas, al menos existe un destructor diseñado correctamente para el pobre vago que me sigue, tendrá uno. (Y, en un valor mucho menor, hace que el código sea más reutilizable, ya que puede usarse en un entorno donde sería destruido).
Si bien eso aborda YAGNI, no aborda el código muerto. Para eso, encuentro que una macro de compilación condicional, como #define BUILD_FOR_TESTING, permite eliminar el destructor de la compilación de producción final.
Haciéndolo de esta manera: tiene un destructor para probar / reutilizar en el futuro, y satisface los objetivos de diseño de YAGNI y las reglas de "sin código muerto".
fuente
Podrías tener un destructor sin operación, algo así como
luego configure el destructor de
Uart
tal vez usando(agregue el yeso adecuado si es necesario)
No te olvides de documentar. Tal vez quieras incluso
Alternativamente, el caso especial en el código común que llama al destructor es el caso cuando la función del puntero del destructor es
NULL
evitar llamarlo.fuente