Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Fundamentos de Programacion con C++ : Casos pr...

Fundamentos de Programacion con C++ : Casos practicos para ingenierıa y ciencias (Parte 2)

Avatar for Abraham Zamudio

Abraham Zamudio

October 02, 2026

More Decks by Abraham Zamudio

Other Decks in Education

Transcript

  1. Fundamentos de Programación con C++ Abraham Zamudio Chauca 2026 De

    código correcto a producto de ingenierı́a Aprendemos Juntos Casos prácticos para ingenierı́a y ciencias
  2. Objetivos Objetivo general. Que los participantes dominen los aspectos fundamentales

    de C++ para implementar soluciones de software aplicables al mundo real. Objetivos especı́ficos. Al finalizar, el participante podrá: • Escribir código correcto con tipos, funciones, estructuras y manejo de errores. • Estructurar código con clases, RAII y la STL. • Reutilizar código con templates, testing y separación de responsabilidades. • Optimizar memoria y rendimiento. • Integrar concurrencia, hardware y GUI. • Construir un producto de ingenierı́a completo. Fundamentos de Programación con C++ Abraham Zamudio Chauca 2 / 34
  3. Ruta de aprendizaje: 6 niveles Nivel 1 Código correcto Nivel

    2 Código estructurado Nivel 3 Código reutilizable Nivel 6 Producto de ingenierı́a Nivel 5 Concurrente e integrable Nivel 4 Código eficiente Cada nivel se apoya en el anterior: primero que funcione, luego que sea mantenible, reutilizable, rápido, integrable y, finalmente, un producto. Fundamentos de Programación con C++ Abraham Zamudio Chauca 3 / 34
  4. Caso práctico transversal Sistema de monitoreo y control de un

    proceso fı́sico • Tanque de nivel con: ◦ Sensor de nivel. ◦ Bomba de entrada. ◦ Válvula de salida. ◦ Controlador PID. • Será el hilo conductor en todos los niveles desarrollados en esta presentación. Modelo: A PID Qin B √ dh = Qin − Cd a 2gh dt LT h(t) V Fundamentos de Programación con C++ Abraham Zamudio Chauca 4 / 34
  5. ¿Qué es código correcto? • Tipos y variables: int, //

    Paso de parámetros void f1(double x); // copia void f2(double& x); // modifica void f3(const std::vector<double>& v); // lee sin copiar double, bool, auto, const, constexpr. // Tres formas de reportar errores • Control de flujo: if, for, double raiz(double x) { // 1) excepción while, switch. if (x < 0) throw std::domain_error("x < 0"); return std::sqrt(x); • Funciones: paso por valor, por } std::optional<double> leer(); // 2) puede no haber valor referencia y por const&. enum class Estado { Ok, Timeout, FueraDeRango }; Estado leer(double& out); // 3) código de error • Estructuras: struct para agrupar datos. • Errores: excepciones, std::optional, códigos de error. Fundamentos de Programación con C++ Abraham Zamudio Chauca 5 / 34
  6. Caso práctico: lecturas de un sensor de temperatura Leer datos,

    validar el rango fı́sico, calcular promedio y desviación, y reportar errores. #include <vector> #include <optional> #include <cmath> #include <stdexcept> Estadistica calcular(const std::vector<double>& lecturas) { std::vector<double> ok; for (double t : lecturas) if (auto v = validar(t)) ok.push_back(*v); struct Estadistica { double media; double desviacion; std::size_t descartadas; }; if (ok.size() < 2) throw std::runtime_error("Lecturas válidas insuficientes"); double suma = 0.0; for (double x : ok) suma += x; const double media = suma / ok.size(); // Rango fı́sico del sensor // (p. ej. -40 a 125 grados C) std::optional<double> validar(double t) { constexpr double T_MIN = -40.0, T_MAX = 125.0; if (std::isnan(t) || t < T_MIN || t > T_MAX) return std::nullopt; // lectura no válida return t; } double acc = 0.0; for (double x : ok) acc += (x - media) * (x media); return {media, std::sqrt(acc / (ok.size() - 1)), // desviación muestral lecturas.size() - ok.size()}; // lecturas descartadas } Fundamentos de Programación con C++ Abraham Zamudio Chauca 6 / 34
  7. Actividad 1: magnitud fı́sica con detección de errores Completar la

    función que calcula el caudal de salida del tanque (Torricelli): p Q = Cd a 2gh • Rechazar h < 0, a ≤ 0 y Cd ∈ / (0, 1]. • Devolver std::nullopt si los datos no son fı́sicos. #include <optional> #include <cmath> constexpr double G = 9.81; // m/sˆ2 // h: nivel [m], a: área orificio [mˆ2], cd: coef. descarga std::optional<double> caudal(double h, double a, double cd ) { // TODO 1: validar que los datos sean fı́sicamente posibles // TODO 2: calcular Q = cd * a * sqrt(2 * G * h) // TODO 3: devolver el resultado } int main() { if (auto q = caudal(1.5, 0.002, 0.62)) std::printf("Q = %.5f mˆ3/s\n", *q); else std::puts("Error: datos no válidos"); } Fundamentos de Programación con C++ Abraham Zamudio Chauca 7 / 34
  8. Transición “Si el código es correcto, el siguiente paso es

    estructurarlo para que sea mantenible.” Nivel 1 −→ Nivel 2: Código estructurado Fundamentos de Programación con C++ Abraham Zamudio Chauca 8 / 34
  9. De funciones a clases • Clase: datos + operaciones que

    los mantienen válidos. • Encapsulación: miembros private, interfaz public. • Invariante: condición que siempre se cumple (p. ej. 0 ≤ h ≤ hmax ). • Constructor establece el invariante; destructor libera recursos. • RAII: el recurso vive exactamente lo que vive el objeto. Fundamentos de Programación con C++ class Tanque { public: Tanque(double area, double hMax) : area_{area}, hMax_{hMax} { if (area <= 0 || hMax <= 0) throw std::invalid_argument("Dimensiones no válidas"); } void setNivel(double h) { if (h < 0 || h > hMax_) throw std::out_of_range("Nivel fuera de rango"); h_ = h; // invariante preservado } double nivel() const { return h_; } double volumen() const { return area_ * h_; } private: double area_, hMax_; double h_ = 0.0; }; Abraham Zamudio Chauca 9 / 34
  10. Librerı́a estándar y buenas prácticas Herramientas de la STL •

    std::vector, std::string: memoria gestionada. • std::unique ptr: propiedad exclusiva. • std::shared ptr: propiedad compartida (usar con moderación). • std::ifstream/ofstream: archivos que se cierran solos. Principios • Responsabilidad única. • Nombres claros (nivelMedido, no x2). • const-correctness: lo que no cambia, es const. Fundamentos de Programación con C++ // Sin RAII: fuga si algo lanza excepción Sensor* s = new Sensor("LT-01"); procesar(*s); // si lanza... delete s; // ...nunca se ejecuta // Con RAII: liberación garantizada auto s = std::make_unique<Sensor>("LT-01"); procesar(*s); // se libera al salir // Archivo: se cierra en el destructor { std::ofstream log{"datos.csv"}; log << "t,nivel\n"; } // <- cierre automático Abraham Zamudio Chauca 10 / 34
  11. Caso práctico: Sensor, Medicion, Procesador struct Medicion { // dato

    simple: struct double valor; std::chrono::system_clock::time_point timestamp ; std::string unidad; }; class Sensor { // lectura, calibración, estado public: enum class Estado { Ok, Falla, SinCalibrar }; explicit Sensor(std::string id) : id_{std::move (id)} {} void calibrar(double ganancia, double offset) { k_ = ganancia; b_ = offset; estado_ = Estado::Ok; } Medicion leer() const { return {k_ * leerCrudo() + b_, std::chrono::system_clock::now(), " m"}; } Estado estado() const { return estado_; } private: double leerCrudo() const; // acceso al hardware std::string id_; double k_ = 1.0, b_ = 0.0; Estado estado_ = Estado::SinCalibrar; class Procesador { // filtrado y estadı́ sticas public: void agregar(const Medicion& m) { datos_. push_back(m.valor); } double media() const; double desviacion() const; private: std::vector<double> datos_; }; class LogCSV { // RAII: archivo de log public: explicit LogCSV(const std::string& ruta) : f_{ruta} { if (!f_) throw std::runtime_error("No se pudo abrir " + ruta); f_ << "timestamp,valor,unidad\n"; } void escribir(const Medicion& m); private: std::ofstream f_; // su destructor cierra el archivo }; }; Fundamentos de Programación con C++ Abraham Zamudio Chauca 11 / 34
  12. Actividad 2: refactorizar a una clase con invariantes Punto de

    partida struct Valvula { double apertura; // 0..100 % }; void abrir(Valvula& v, double p) { v.apertura = p; // ¡sin validar! } double caudal(const Valvula& v, double qMax) { return qMax * v.apertura / 100.0; } Fundamentos de Programación con C++ Tarea 1. Convertir Valvula en clase. 2. Garantizar el invariante 0 ≤ apertura ≤ 100. 3. Recibir qMax en el constructor y validarlo. 4. Marcar como const los métodos que no modifican. Abraham Zamudio Chauca 12 / 34
  13. Transición “El código estructurado se puede reutilizar si separamos interfaces

    e implementaciones.” Nivel 2 −→ Nivel 3: Código reutilizable Fundamentos de Programación con C++ Abraham Zamudio Chauca 13 / 34
  14. Genéricos y templates Separación .h / .cpp • .h: declaraciones

    (la interfaz). • .cpp: definiciones (la implementación). • Los templates se definen en el .h: el compilador necesita verlos para instanciarlos. // Función template template <typename T> T limitar(T x, T lo, T hi) { return x < lo ? lo : (x > hi ? hi : x); } // Concepto (C++20) #include <concepts> template <typename T> concept Numerico = std::integral<T> || std:: floating_point<T>; // Clase template restringida template <Numerico T> class Rango { public: Rango(T lo, T hi) requires (sizeof(T) <= 8) : lo_{lo}, hi_{hi} {} bool contiene(T x) const { return lo_ <= x && x <= hi_; } private: T lo_, hi_; }; Fundamentos de Programación con C++ include/procesador.h src/procesador.cpp include/estadistica.h (templates) Abraham Zamudio Chauca 14 / 34
  15. Testing y separación de responsabilidades • Unit testing: Catch2, GoogleTest,

    doctest. • Interfaces + mocks: probar sin hardware real. • Diseño por capas: ◦ Dominio: modelo del tanque, PID, estadı́stica. ◦ Infraestructura: puerto serie, archivos, red. ◦ Presentación: consola, GUI, web. • El dominio no depende de la infraestructura. Fundamentos de Programación con C++ // Interfaz: el dominio sólo conoce esto class ISensorNivel { public: virtual ˜ISensorNivel() = default; virtual double leerNivel() = 0; }; // Implementación real (infraestructura) class SensorSerie : public ISensorNivel { double leerNivel() override; // lee puerto }; // Mock para pruebas class SensorFalso : public ISensorNivel { public: double valor = 1.0; double leerNivel() override { return valor; } }; Abraham Zamudio Chauca 15 / 34
  16. Caso práctico: estadı́stica genérica, filtro y tests template <Numerico T>

    double promedio(std::span<const T> d) { if (d.empty()) throw std::invalid_argument("vacı́o"); return std::accumulate(d.begin(), d.end(), 0.0) / d. size(); } template <Numerico T> double mediana(std::vector<T> d) { // copia a propósito if (d.empty()) throw std::invalid_argument("vacı́o"); auto mid = d.begin() + d.size() / 2; std::nth_element(d.begin(), mid, d.end()); if (d.size() % 2) return *mid; return (*std::max_element(d.begin(), mid) + *mid) / 2.0; } // Filtro de media móvil genérico template <Numerico T, std::size_t N> class MediaMovil { public: double filtrar(T x) { suma_ += x - buf_[i_]; buf_[i_] = x; i_ = (i_ + 1) % N; if (n_ < N) ++n_; return suma_ / n_; } private: std::array<T, N> buf_{}; std::size_t i_ = 0, n_ = 0; double suma_ = 0.0; }; Fundamentos de Programación con C++ #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include <doctest/doctest.h> TEST_CASE("promedio de enteros") { std::vector<int> v{1, 2, 3, 4}; CHECK(promedio<int>(v) == doctest:: Approx(2.5)); } TEST_CASE("mediana par e impar") { CHECK(mediana<double>({3, 1, 2}) == 2.0); CHECK(mediana<double>({4, 1, 3, 2}) == 2.5); } TEST_CASE("Procesador usa el sensor") { SensorFalso s; s.valor = 2.0; Procesador p; p.agregar({s.leerNivel(), {}, "m"}); CHECK(p.media() == doctest::Approx (2.0)); } Abraham Zamudio Chauca 16 / 34
  17. Actividad 3: template de estadı́stica + test Tarea 1. Implementar

    rango<T>(): diferencia entre máximo y mı́nimo. 2. Restringirlo con el concepto Numerico. 3. Escribir al menos dos tests: caso normal y vector vacı́o. Fundamentos de Programación con C++ template <Numerico T> T rango(std::span<const T> d) { // TODO: lanzar excepción si d está vacı́o // TODO: usar std::minmax_element } TEST_CASE("rango de niveles") { std::vector<double> h{0.8, 1.2, 0.5, 1.0}; // TODO: CHECK(...) } TEST_CASE("rango de vector vacı́o") { std::vector<int> v; // TODO: CHECK_THROWS_AS(...) } Abraham Zamudio Chauca 17 / 34
  18. Transición “Código reutilizable, pero ¿es suficientemente rápido para datos reales?”

    Nivel 3 −→ Nivel 4: Código eficiente Fundamentos de Programación con C++ Abraham Zamudio Chauca 18 / 34
  19. Memoria y rendimiento • Stack: rápido, tamaño fijo, vida automática.

    • Heap: flexible, más costoso, vida explı́cita. • new/delete manuales ⇒ fugas y dobles liberaciones. Preferir contenedores y smart pointers. • Move semantics: transferir recursos en lugar de copiarlos. • Localidad de caché: datos contiguos (vector) ≫ datos dispersos (list). Fundamentos de Programación con C++ void ejemplo() { double a[1024]; // stack std::vector<double> v(1’000’000); // heap, // gestionado } // ambos se liberan aquı́ std::vector<double> generar() { std::vector<double> d(1’000’000); return d; // move (o elisión): sin copia } std::vector<double> a = generar(); std::vector<double> b = std::move(a); // a queda vacı́o pero válido; b tiene los datos Abraham Zamudio Chauca 19 / 34
  20. Medición y optimización Regla de oro: medir antes de optimizar.

    • Benchmarks con std::chrono. • Profiling: perf, gprof, Valgrind/Callgrind, VTune. • Elegir bien algoritmo y estructura de datos (O (n) vs O (n2 )). • Evitar copias: const&, std::move, emplace back. • Compilar con optimización: -O2/-O3. Fundamentos de Programación con C++ #include <chrono> template <typename F> double medir_ms(F&& f, int repeticiones = 10) { using reloj = std::chrono::steady_clock; auto t0 = reloj::now(); for (int i = 0; i < repeticiones; ++i) f(); auto t1 = reloj::now(); return std::chrono::duration<double, std::milli>( t1 - t0).count() / repeticiones; } // Uso double ms = medir_ms([&] { procesar(muestras); }); std::printf("%.3f ms por ejecución\n", ms); Abraham Zamudio Chauca 20 / 34
  21. Caso práctico: 1 millón de muestras constexpr std::size_t N =

    1’000’000; // Versión ingenua std::list<double> l; for (std::size_t i = 0; i < N; ++i) l.push_back(leer(i)); double s1 = 0; for (double x : l) s1 += x; // Versión idiomática std::vector<double> v; v.reserve(N); // una sola reserva for (std::size_t i = 0; i < N; ++i) v.push_back(leer(i)); double s2 = std::accumulate(v.begin(), v.end(), 0.0); // Filtrado sin bucles manuales auto fuera = std::count_if(v.begin(), v.end(), [](double h) { return h < 0.0 || h > 2.0; }); // Transformación in situ (calibración) std::transform(v.begin(), v.end(), v.begin(), [k = 1.02, b = -0.01](double x) { return k * x + b; }); Fundamentos de Programación con C++ Qué observar • list: un nodo por elemento en el heap ⇒ fallos de caché. • vector: memoria contigua, el prefetcher ayuda. • reserve evita realocaciones sucesivas. • Algoritmos STL: más legibles y fáciles de optimizar para el compilador. Demo: comparar tiempos con medir ms usando -O0 y -O2. Abraham Zamudio Chauca 21 / 34
  22. Actividad 4: micro-benchmark // Versión A: ¿dónde están las copias?

    double energia(std::vector<double> senal) { std::vector<double> cuadrados; for (auto x : senal) cuadrados.push_back(x * x); double e = 0; for (auto c : cuadrados) e += c; return e; } // Versión B: TODO - optimizar double energia_opt(/* ? */) { // ... } Fundamentos de Programación con C++ Tarea 1. Medir la versión A con medir ms. 2. Escribir la versión B: sin copias, sin vector auxiliar (pista: std::inner product o transform reduce). 3. Medir y reportar el speedup. 4. Verificar que ambas den el mismo resultado. Abraham Zamudio Chauca 22 / 34
  23. Transición “El código eficiente debe integrarse con hardware, otros hilos

    y sistemas externos.” Nivel 4 −→ Nivel 5: Código concurrente e integrable Fundamentos de Programación con C++ Abraham Zamudio Chauca 23 / 34
  24. Concurrencia • std::thread / std::jthread: hilos de ejecución. • std::mutex

    + std::lock guard: exclusión mutua (RAII). • std::atomic: variables sin carreras para tipos simples. • std::async, std::future: tareas con resultado. • Condición de carrera: dos hilos acceden a un dato y al menos uno escribe, sin sincronización. • Deadlock: dos hilos se esperan mutuamente. Solución: orden fijo de bloqueo o std::scoped lock. Fundamentos de Programación con C++ // Carrera: resultado impredecible int contador = 0; auto inc = [&] { for (int i = 0; i < 100000; ++i) ++ contador; }; std::thread t1(inc), t2(inc); t1.join(); t2.join(); // ¿200000? no siempre // Corrección 1: atomic std::atomic<int> c{0}; // Corrección 2: mutex std::mutex m; { std::lock_guard<std::mutex> lk(m); ++contador; } // Tarea ası́ncrona con resultado std::future<double> f = std::async(std::launch::async, [&] { return mediana(muestras); }); double med = f.get(); // espera el resultado Abraham Zamudio Chauca 24 / 34
  25. Integración con hardware y sistemas Adquisición de datos • Tarjetas

    DAQ, microcontroladores, PLC. • Muestreo periódico y timestamps. Protocolos • Serie (RS-232/485, USB-CDC): Boost.Asio. • Modbus RTU/TCP: libmodbus. • MQTT: Eclipse Paho, Mosquitto. • TCP/IP: sockets, Boost.Asio. Fundamentos de Programación con C++ Interfaces gráficas • Qt: aplicaciones completas, gráficos con Qt Charts. • Dear ImGui + ImPlot: paneles rápidos de ingenierı́a. • Web: servidor embebido + navegador. Tiempo real • Plazo determinista: evitar new, E/S bloqueante y locks largos en el lazo de control. • Prioridades de hilo, RTOS o Linux PREEMPT RT. Abraham Zamudio Chauca 25 / 34
  26. Caso práctico: tres hilos y una cola segura template <typename

    T> class ColaSegura { public: void push(T v) { { std::lock_guard lk(m_); q_.push(std:: move(v)); } cv_.notify_one(); } T pop() { // bloquea hasta tener dato std::unique_lock lk(m_); cv_.wait(lk, [&] { return !q_.empty(); }) ; T v = std::move(q_.front()); q_.pop(); return v; } private: std::queue<T> q_; std::mutex m_; std::condition_variable cv_; }; ColaSegura<Medicion> mediciones; ColaSegura<Estado> estados; std::jthread adq([&](std::stop_token st) { while (!st.stop_requested()) { mediciones.push(sensor.leer()); std::this_thread::sleep_for(100ms); } }); Fundamentos de Programación con C++ Abraham Zamudio Chauca Hilo de adquisición Cola mediciones Hilo de cálculo y control Cola estados Hilo de visualización y registro 26 / 34
  27. Actividad 5: arquitectura concurrente Tarea (en papel o pizarra) 1.

    Dibujar los hilos del sistema del tanque y los datos que comparten. 2. Marcar cada dato compartido: ¿quién lee?, ¿quién escribe? 3. Identificar al menos dos posibles condiciones de carrera. 4. Proponer el mecanismo de sincronización: cola, mutex o atomic. 5. ¿Existe riesgo de deadlock? ¿Cómo lo evitarı́as? Fundamentos de Programación con C++ Pistas • El setpoint lo cambia la GUI y lo lee el control. • El archivo de log lo escriben varios hilos. • La bandera “parada de emergencia” la leen todos. • Los parámetros del PID se ajustan en caliente. Abraham Zamudio Chauca 27 / 34
  28. Transición “Ahora integramos todo en un producto de ingenierı́a.” Nivel

    5 −→ Nivel 6: Producto de ingenierı́a Fundamentos de Programación con C++ Abraham Zamudio Chauca 28 / 34
  29. ¿Qué es un producto de ingenierı́a? Modelo fı́sico o matemático

    Cálculo y simulación Visualización Validación Control (opcional) • No es sólo código que compila: es software verificable, documentado y reproducible. • Alguien distinto del autor puede compilarlo, ejecutarlo y confiar en sus resultados. Fundamentos de Programación con C++ Abraham Zamudio Chauca 29 / 34
  30. Arquitectura del producto • Interfaces claras entre módulos Adquisición Modelo

    Visualización Control App / main Persistencia Fundamentos de Programación con C++ (clases abstractas o conceptos). • Pruebas de integración: modelo + PID en lazo cerrado. • Documentación: README, Doxygen. • Empaquetado: CMake + vcpkg/Conan, CI. tanque/ |-- CMakeLists.txt |-- README.md |-- include/ src/ |-- tests/ |-- apps/simulador.cpp Abraham Zamudio Chauca 30 / 34
  31. Caso práctico completo: control de nivel de tanque class PID

    { public: PID(double kp, double ki, double kd, double uMin, double uMax) : kp_{kp}, ki_{ki}, kd_{kd}, uMin_{uMin}, uMax_{uMax} {} double calcular(double ref, double y, double dt ) { double e = ref - y; integral_ += e * dt; double d = (e - ePrev_) / dt; ePrev_ = e; double u = kp_*e + ki_*integral_ + kd_*d; if (u > uMax_ || u < uMin_) integral_ -= e * dt; // anti-windup return std::clamp(u, uMin_, uMax_); } private: double kp_, ki_, kd_, uMin_, uMax_; double integral_ = 0.0, ePrev_ = 0.0; }; Validación: error en régimen permanente, sobreimpulso y tiempo de establecimiento. Fundamentos de Programación con C++ // Modelo: A dh/dt = Qin - Cd a sqrt(2 g h) class ModeloTanque { public: ModeloTanque(double A, double a, double cd) : A_{A}, a_{a}, cd_{cd} {} double paso(double qIn, double dt) { // Euler double qOut = cd_*a_*std::sqrt(2*9.81* h_); h_ = std::max(0.0, h_ + dt*(qIn - qOut )/A_); return h_; } private: double A_, a_, cd_, h_ = 0.0; }; int main() { ModeloTanque tanque{1.0, 0.01, 0.62}; PID pid{2.0, 0.5, 0.1, 0.0, 0.05}; LogCSV log{"simulacion.csv"}; const double ref = 1.2, dt = 0.1; // [m], [s ] double h = 0.0; for (int k = 0; k < 3000; ++k) { double q = pid.calcular(ref, h, dt); h = tanque.paso(q, dt); log.escribir({h, {}, "m"}); } } Abraham Zamudio Chauca 31 / 34
  32. Resumen de la ruta Nivel 1. Correcto 2. Estructurado 3.

    Reutilizable 4. Eficiente 5. Concurrente 6. Producto Habilidad adquirida Tipos, funciones, struct, validación y manejo de errores. Clases con invariantes, RAII, STL, const-correctness. Templates, conceptos, interfaces, unit testing. Memoria, move semantics, caché, benchmarks. Hilos, sincronización, protocolos, GUI. Arquitectura, integración, documentación, empaquetado. De código que funciona a software en el que se puede confiar. Fundamentos de Programación con C++ Abraham Zamudio Chauca 32 / 34
  33. Buenas prácticas y recursos Lecturas • S. Meyers, Effective C++

    y Effective Modern C++. • B. Stroustrup, A Tour of C++. • C++ Core Guidelines (Stroustrup & Sutter). Fundamentos de Programación con C++ Herramientas • CMake. • vcpkg, Conan. • Sanitizers: ASan, UBSan, TSan. • clang-tidy, clang-format. Abraham Zamudio Chauca Comunidad • cppreference.com • Stack Overflow. • GitHub. • Compiler Explorer (godbolt.org). 33 / 34
  34. Próximos pasos • Proyecto final: completar el sistema de control

    de nivel con tests, README y demo. • Lecturas recomendadas: A Tour of C++ y las secciones de recursos (R), funciones (F) y concurrencia (CP) de las C++ Core Guidelines. • Práctica: compilar siempre con -Wall -Wextra y los sanitizers activados. Fundamentos de Programación con C++ Abraham Zamudio Chauca 34 / 34