Rutas
/
C++
C++
/
Ejercicios
/
Fundación Speedywagon
Fundación Speedywagon

Fundación Speedywagon

Ejercicio de aprendizaje

Introducción

Como muchos otros lenguajes, C++ tiene punteros. Ya sabes que las referencias y los punteros son similares, pero piensa en ellos como un nivel más cerca del funcionamiento interno de tu ordenador. Los punteros son variables que contienen direcciones de objetos. Se utilizan para interactuar directamente con los objetos, lo que permite la asignación dinámica de memoria y una manipulación eficiente de los datos en C++.

Si eres nuevo en los punteros, pueden parecer un poco misteriosos, pero una vez que te acostumbras a ellos, son bastante sencillos.

Son una parte crucial de C++, así que tómate tu tiempo para entenderlos bien. La versión más básica de este concepto también se conoce como dumb pointer o raw pointer. Con el C++ moderno también existen los smart pointers; el tipo básico no es nada inteligente y tienes que encargarte de todo el trabajo manualmente.

Antes de profundizar en los detalles, merece la pena entender el uso de los punteros. Los punteros son una forma de compartir la dirección de un objeto con otras partes de nuestro programa, lo cual es útil por dos razones principales:

  1. Como las referencias, los punteros evitan copias y ayudan a reducir el consumo de recursos de tu programa.
  2. A diferencia de las referencias, los punteros se pueden reasignar a objetos diferentes.
  3. Los punteros también pueden apuntar a un valor nulo, para indicar que actualmente no apuntan a ningún objeto.

Sintaxis general

Una declaración de puntero en C++ implica especificar el tipo de dato al que apunta el puntero, seguido de un asterisco (*) y el nombre del puntero. Cuando se declaran punteros, no se inicializan automáticamente. Sin una asignación explícita, un puntero suele contener un valor indeterminado, al que a menudo se denomina «dirección basura». Aunque algunos compiladores podrían inicializar los punteros a nullptr, este comportamiento no está garantizado en todos los compiladores, por lo que es esencial no confiar en él. Es una buena práctica inicializar explícitamente los punteros raw y verificar que no sean nulos antes de usarlos, para evitar posibles problemas.

int* ptr{nullptr}; // Declares a pointer and makes sure it is not invalid

Para asignar la dirección de una variable a un puntero, se utiliza el operador de dirección (&). La desreferenciación de un puntero se realiza mediante el operador de indirección (*).

std::string opponent{"Solomon Lane"};
// 'ethan' points to the address of the string opponent
std::string* ethan{&opponent};
// Instead of ethan's, the opponent's name address is given to the passPort
std::string passportName{*ethan};

Atención: la desreferenciación debe hacerse explícitamente, mientras que las referencias simplemente funcionan como un alias.

Aritmética de punteros

La aritmética de punteros te permite realizar operaciones aritméticas con punteros, lo cual es especialmente útil cuando se trabaja con arrays. Sumar un número entero a un puntero hace que apunte a un elemento diferente.

// Stargate Coordinate Code
int gateCode[] = {462, 753, 218, 611, 977};
// 'ptr' points to the first element of 'gateCode'
int* ptr{&gateCode[0]};
// Accesses the third Stargate address through pointer arithmetic
int dialedAddress{*(ptr + 2)};
// Chevron encoded! Dialing Stargate address:
openStarGate(dialedAddress);
Caution

La aritmética de punteros en C++ puede provocar fácilmente un comportamiento indefinido si no se maneja con cuidado. El comportamiento indefinido puede manifestarse en resultados inesperados del programa, bloqueos o incluso vulnerabilidades de seguridad. Un ejemplo famoso de las consecuencias del comportamiento indefinido ocurrió en la explosión del cohete Ariane 5 en 1996, cuando una excepción de software causada por la conversión de un número de coma flotante de 64 bits a un entero con signo de 16 bits provocó un fallo catastrófico.

Acceder a las variables miembro

En C++, el operador -> se utiliza para acceder a los miembros de un objeto a través de un puntero a ese objeto. Es una forma abreviada que simplifica el acceso a los miembros de los objetos a los que apuntan los punteros. Por ejemplo, si ptr es un puntero a un objeto con una variable miembro x, en lugar de usar (*ptr).x, puedes usar directamente ptr->x. Este operador mejora la legibilidad del código y reduce la verbosidad cuando se trabaja con punteros a objetos.

Aquí tienes un breve ejemplo, con una struct Superhero que tiene una variable miembro superpower. La función main crea un puntero dianaPrince a un objeto Superhero (que representa a Wonder Woman). Se utiliza el operador -> para acceder a la variable miembro superpower, mostrando el icónico «Lasso of Truth» de Wonder Woman.

struct Superhero {
    std::string superpower;
};
Superhero wonder_woman{};
Superhero* dianaPrince = &wonder_woman;
dianaPrince->superpower = "Lasso of Truth";
// Using the -> operator to access member variable superpower:
std::cout << "Wonder Woman, possesses the mighty " << dianaPrince->superpower;

Punteros frente a referencias

Tanto los punteros como las referencias permiten el acceso indirecto a objetos, pero difieren en sus capacidades y consideraciones de seguridad. Los punteros ofrecen la flexibilidad de cambiar el objeto al que apuntan y pueden asignarse a nulo. Sin embargo, esta flexibilidad introduce riesgos, como desreferenciar punteros nulos o crear punteros colgantes. Las referencias, en cambio, no pueden ser nulas y se vinculan a objetos válidos en el momento de su creación, evitando estos riesgos. Dada su naturaleza más segura, deberían preferirse las referencias a los punteros, a menos que sean necesarias las funcionalidades adicionales que ofrecen los punteros.

Instrucciones

¡Bienvenido, ingeniero! Eres uno de los últimos veteranos de la Speedywagon Foundation, una organización secreta que, durante décadas, ha combatido amenazas antiguas como los Pillar Men. En el transcurso de este esfuerzo, has pasado años manteniendo los sistemas tecnológicos de la Foundation, construidos con una mezcla de tecnología de vanguardia y bibliotecas anticuadas.

Sin embargo, en los últimos tiempos, los sensores que siguen la actividad de los Pillar Men están fallando. Los sistemas de la Foundation son antiguos, y el código interactúa con una biblioteca heredada de C++ que no se puede actualizar. Tu tarea es implementar cuatro funciones fundamentales que monitorizan la actividad de los sensores de los Pillar Men usando una biblioteca anticuada basada en punteros.

Como ingeniero moderno de C++, preferirías usar punteros inteligentes, pero, ay, el código heredado exige respeto por las viejas costumbres. El destino de la humanidad puede depender de estos punteros, así que procede con cuidado, y que la energía Hamon te guíe.

Note

Como las lecturas de los sensores pueden ser enormes, te proporcionamos un struct de ejemplo que se usa en la biblioteca real. El código ya está implementado en el archivo de cabecera para ti.

struct pillar_men_sensor {
    int activity{};
    std::string location{};
    std::vector<int> data{};
};

1. Comprobar la conexión del sensor (connection_check)

Tu primera tarea es asegurarte de que el sensor de los Pillar Men está conectado correctamente. No podemos permitir falsas alarmas provocadas por sensores desconectados. Escribirás una función connection_check, que comprueba si el puntero del sensor es válido verificando si es nullptr.

  • Define una función que acepte un puntero a un struct pillar_men_sensor.
  • La función debe devolver true si el puntero del sensor no es nulo, y false en caso contrario.
pillar_men_sensor* sensor{nullptr};
bool isConnected = connection_check(sensor);
// isConnected => false

2. Contar la actividad de los sensores (activity_counter)

Los Pillar Men acechan en las sombras, y necesitamos saber si los sensores han detectado alguna actividad. Escribirás la función activity_counter, que recibe un array de sensores y una capacidad que indica el número de sensores del array.

  • Define una función que acepte un puntero al primer elemento de un array y la capacidad del array.
  • Usa aritmética de punteros para recorrer con un bucle el array de sensores y acumular las lecturas de actividad.
  • Devuelve la actividad acumulada.
pillar_men_sensor sensor_array[3] = {{0}, {101}, {22}};
int totalActivity = activity_counter(sensor_array, 3);
// totalActivity => 123

3. Control de la alarma (alarm_control)

No todos los sensores deberían activar una alarma a menos que haya un peligro real. La función alarm_control garantiza que un sensor solo active una alarma si su nivel de actividad es mayor que 0. Esta función también debe comprobar si hay sensores nulos para evitar que el sistema se bloquee.

  • Define una función que acepte el puntero a un pillar_men_sensor.
  • La función debe comprobar primero si el sensor es nullptr. Si el sensor es nullptr, devuelve false.
  • Si el sensor es válido y su actividad es mayor que 0, devuelve true; en caso contrario, devuelve false.
pillar_men_sensor db{9008, "songokunoie", {7, 7, 7}};
bool alarm = alarm_control(&db);
// alarm => true

4. Comprobar los datos en busca de anomalías con la función uv_alarm

En esta tarea, implementarás la función uv_alarm para determinar si debe activarse una alarma en función de los niveles de exposición a la luz UV y de la actividad del sensor. La función uv_alarm debe usar la función uv_light_heuristic proporcionada, que opera sobre un vector de datos y devuelve un valor en función de ciertos umbrales. Esta es una versión de ejemplo del código complejo que se ejecutará en producción; por favor, no cambies la interfaz.

Define la función uv_alarm en el espacio de nombres speedywagon. Debe:

  • Recibir como parámetro un puntero a un struct pillar_men_sensor.
  • Devolver false si el puntero del sensor es nulo.
  • Llamar a la función uv_light_heuristic, pasando la dirección del array data del sensor.
  • Devolver true si el valor devuelto por uv_light_heuristic es mayor que el nivel sensor->activity; en caso contrario, devuelve false.
Editar en GitHub El enlace se abre en una ventana o pestaña nueva
C++ Exercism

¿Listo para empezar Fundación Speedywagon?

Regístrate en Exercism para aprender y dominar C++ con 19 conceptos100 ejercicios y mentoría humana real, todo gratis.