C++ : new, delete, excepciones, std::terminate y RAII. Asegurando liberación de recursos

Поделиться
HTML-код
  • Опубликовано: 29 сен 2024
  • [C++ 2.4]: Serie completa bit.ly/CppGameD...
    Serie anterior C++: bit.ly/CppDev20...
    Librería TinyPTC UA: bit.ly/tinyPTC-...
    Trabajamos las diferencias entre reservar memoria a mano y hacerlo de forma gestionada a través de constructores y destructores de una clase. Entendemos las garantías que ofrecen los destructores en C++, cómo son llamados siempre que se produce el rebobinado de la pila ante una excepción, y cómo eso puede suceder o no ante excepciones no controladas en una llamada a std::terminate. Con esto entendemos las ventajas y necesidades del patrón RAII (Resource Acquisition Is Initialization). Usamos también valgrind, entendemos detalles de los constructores, gestionamos excepciones de manera básica y comprendemos la importancia de los memory leaks.
    Contenidos detallados:
    - Reservando memoria en el #heap a mano con #new y #delete.
    - Usando valgrind para verificar el uso apropiado de la memoria reservada en el heap.
    - Entendiendo las #excepciones como forma de separar el código principal del código destinado a la gestión de errores.
    - Analizando lo que sucede cuando se lanza una excepción (simulándolo con throw).
    - Memory leaks producidos por una excepción al alterar el flujo normal de la ejecución del programa.
    - Uso de los destructores como liberación garantizada de recursos
    - Creando una clase usando el patrón RAII para que se encargue de reservar la memoria en el constructor y liberarla en el destructor, garantizando su liberación incluso en casos de excepciones.
    - Pregunta : Diferencia entre usar constructores delegados en un constructor e inicializar atributos usando =. Diferencias entre constructor y operador asignación.
    - Problema : las excepciones no controladas llaman a std::terminate. La implementación de std::terminate puede o no rebobinar la pila (stack unwinding). Si no lo hace, no se llama a los destructores
    - Entendiendo los problemas que pueden haber de depender de alguna característica que puede variar entre implementaciones (como el stack unwinding dependiente de std::terminate)
    - Capturando excepciones y entendiendo cómo funcionan.
    - Observando cómo funciona todo al capturar excepciones. La captura de excepciones garantiza el rebobinado de la pila porque se produce una salida progresiva ordenada de los ámbitos hasta encontrar el catch.
    - Qué sucede con la memoria no liberada por un proceso y por qué es importante.
    Clase de Videojuegos 1 2019/20
    Grado en Ingeniería Multimedia
    Universidad de Alicante

Комментарии • 14

  • @d4ny3l23
    @d4ny3l23 4 года назад +5

    Hola Profe, como puedo usar Valgrind en windows ?? he buscado y no encuentro. me aria falta Valgrind para ayudarme a saber esas cosas de perdidas.

    • @ProfesorRetroman
      @ProfesorRetroman  4 года назад +3

      Lo lamento, pero no puedes usar Valgrind en Windows, porque sólo es para Linux. Lo que puedes hacer es usar Valgrind a través de sistemas de emulación de Linux en Windows, como el WSL o Cygwin, pero Valgrind es de Linux. En Windows tienes que usar otros sistemas para supervisar la gestión de memoria.

  • @ninfarave
    @ninfarave 2 года назад +2

    Hola. Excelente video. Tengo entendido que la excepciones de hardware. Como null access pointer, division by zero. No pueden ser capturadas con el try catch de C++. Se que es posible capturar estas interrupciones de linux/windows con EEH y transformarlas en excepciones de c++. Sin embargo esto solo funciona con gcc/msvc y no con clang. Pregunto esto por que un backend http el programa no puede caerse por este tipo de excepciones. Por que debe devolver al cliente http una repuesta, como un http 500. Y si hay otras peticiones siendo atendidas por otras hebras pues también las dejaría sin repuesta. ¿Usted tiene alguna idea de por que en clang no funcionan? ¿Será un bug? o ¿Algo de diseño?. En clang solo funciona setjmp longjmp pero no puedo convertirlas en excepciones del C++.

    • @ProfesorRetroman
      @ProfesorRetroman  2 года назад +2

      Gracias por el aprecio :).
      Esto que llamas "excepciones hardware" son en realidad bugs en tu código. Tu código no debe nunca dividir por cero o desreferenciar un nullptr. Si lo hace, es un bug en tu código, no una excepción como concepto del lenguaje de C++. Esto no es propio de C++, sino común a cualquier lenguaje nativo. Si un lenguaje te permite "capturar" estos problemas, necesariamente es un lenguaje gestionado o interpretado, para poder capturarlos antes de que se ejecuten en el hardware. No es tanto por el lenguaje pues, como por su implementación. Si se hiciera una "máquina virtual de C++" también podría "capturar" esto. De hecho, eso es lo que hacen softwares como el Address Sanitizer (integrado tanto en Clang como en GCC) o Valgrind. Estos softwares introspectivos bien introducen instrumentalización del código (gestión) o simulan una máquina virtual (Valgrind) para detectar tus errores y reportártelos. Sin embargo, tampoco su intención es la de que puedas capturarlos: sólo la de que puedas conocerlos para corregirlos, pues son bugs.
      Sin embargo, no veo que sea una buena idea en absoluto intentar irse al nivel del procesador para capturar estos problemas. No es esa la forma de evitar que tu servidor http se caiga. La forma es que tu servidor no tenga bugs. Los bugs deben ser identificados y corregidos, no circunvenidos. Implementar técnicas de este estilo, además de ser un manejo directo del hardware que el SO puede no permitir, podría tener efectos laterales sobre el resto de procesos de la máquina. No es una buena práctica. Además, produciría que dejes de prestar atención a tus bugs, ya que no los verías suceder. Por otra parte, haciendo esto abrirías la puerta a la explotación de esos bugs como punto de entrada para atacantes, ya que podrían ganar acceso a la máquina a partir de estos saltos introducidos.
      Sin duda alguna, no creo que sea una buena idea en modo alguno hacer esto en un entorno de producción o con una finalidad como esta. Para estos problemas, lo lógico, es identificarlos en el ciclo de desarrollo, depurarlos y corregirlos, ya que son bugs.

    • @SLTRM
      @SLTRM 2 года назад +2

      Considera usar GraalVM java native_image. Compila a nativo código máquina. Maneja mejor la excepciones para poder depurar mejor.

    • @ninfarave
      @ninfarave 2 года назад

      @@SLTRM gracias por tu sugerencia, veré que hacer, la verdad solo quería reescribir un backend usando C++ pero no se ya si sea la mejor opción o no. Native Image maneja los null pointer access con try catch?

    • @SLTRM
      @SLTRM 2 года назад

      @@ninfarave Si todo igual a el java de jvm. Solo que ahora es compilado a código máquina como lo hace c/c++. Puedes capturar en el catch esos errores que describes y enviarlos a herramientas como rollbar loggy sumologic y otras usando un cliente http. C# tiene otro proyecto AOT compiler, pero es un alfa y no es para producción todavía.

  • @diegorfuentes
    @diegorfuentes 4 года назад +5

    Gracias por el vídeo. Y el Leak al final es porque si se lanza la excepción, en el catch no se hace el ptc_close();

  • @ninfarave
    @ninfarave 2 года назад +1

    🖖

  • @aibou2399
    @aibou2399 4 года назад +3

    Excelente vídeo, se agradece el trabajo. Y que curioso que la librería X11 tenga leaks. ¿Eso es porque es una librerìa escrita en C, y no existe RAII en C?

    • @ProfesorRetroman
      @ProfesorRetroman  4 года назад +5

      Bueno, realmente no tiene leaks. Forzamos la aparición de leaks al lanzar excepciones e impedir su cierre como es debido por la interrupción del flujo normal de ejecución. Efectivamente, se debe a que xlib está en C. No es nada muy relevante tampoco: poco más que una mera curiosidad. Si hay leaks en otras librerías más recientes y sobre todo problemas en los drivers gráficos en general. Al usar OpenGL, por ejemplo, aparecen muchos de este estilo dependiendo de los drivers que tengamos.

  • @sherpad01
    @sherpad01 3 года назад +2

    Gran vídeo, tengo una pequeña duda. Dices que el caso de pasar el ancho y alto por valor para reservar la memoria para screen es más rápido que pasarlo por referencia al ser valores básicos de 32 bits. ¿En que casos conviene pasar parámetros por valor y en cuales por referencia si solo los vas a usar para lectura? Muchas gracias.

    • @ProfesorRetroman
      @ProfesorRetroman  3 года назад +3

      Es una buena pregunta, y este es uno de esos casos donde siempre comento que es necesario saber lo que pasa a bajo nivel para programar bien a alto nivel. El tema de pasar por valor o referencia puede requerir a veces copiar grandes cantidades de valores (paso por valor) o simplemente dar una dirección de memoria (paso por referencia). Sin embargo, hay ocasiones en que la copia por valor cabe en los registros de la CPU, pudiendo ser más eficiente que el paso por referencia ya que la copia es más rápida que los posteriores accesos indirectos. No hay una regla general para esto: depende de la CPU y del funcionamiento de paso de valores entre funciones. Conociendo cómo funciona y cómo es tu CPU, puedes ver el ensamblador generado y entender qué opción es mejor. Aprenderse reglas no es buena idea, porque las reglas son dependientes siempre de las circunstancias: hay que entender los principios para saber valorarlo en cada situación.
      En estas situaciones concretas, cuando el tamaño del valor a pasar es igual o inferior al tamaño de un puntero en la máquina, se puede decir que suele ser mejor pasar por valor que por referencia, en cuanto a eficiencia se refiere. Pero, como te decía antes, no lo tomes como una regla. Evita siempre las reglas mnemotécnicas, trabaja para entender los detalles. Eso te permitirá poder decidir tú mismo en cada situación qué es mejor, basándote en conocimiento y no en reglas limitadas.

  • @silose
    @silose 4 года назад +1

    👍, Saludos.