Tracks
/
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 computadora. Los punteros son variables que contienen direcciones de objetos. Se usan 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 es la primera vez que usas 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 dedícales un tiempo para entenderlos de verdad. La versión más básica de este concepto también se llama puntero tonto o puntero crudo. Con el C++ moderno también existen los punteros inteligentes; el tipo básico no es nada inteligente y tienes que encargarte de todo el trabajo manualmente.

Antes de profundizar en los detalles, vale 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. Al igual que las referencias, los punteros evitan copias y ayudan a reducir la huella 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 los punteros, no se inicializan automáticamente. Sin una asignación explícita, un puntero suele contener un valor indeterminado, al que con frecuencia se le llama «dirección basura». Aunque algunos compiladores podrían inicializar los punteros a nullptr, este comportamiento no está garantizado en todos los compiladores, así que es esencial no confiar en ello. Es una buena práctica inicializar explícitamente los punteros crudos 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, usas el operador de dirección (&). Para desreferenciar un puntero se usa 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 de forma explícita, mientras que las referencias simplemente funcionaban 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 trabajas con arrays. Sumar un 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 llevar fácilmente a un comportamiento indefinido si no se maneja con cuidado. El comportamiento indefinido puede manifestarse en resultados inesperados del programa, fallos, o incluso vulnerabilidades de seguridad. Un ejemplo tristemente célebre de las consecuencias del comportamiento indefinido ocurrió en la explosión del cohete Ariane 5 en 1996, donde una excepción de software causada por la conversión de un número de punto 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 usa 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 trabajas con punteros a objetos.

Aquí tienes un ejemplo breve, 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). El operador -> se usa para acceder a la variable miembro superpower, que muestra 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 vs. referencias

Los punteros y las referencias permiten ambos el acceso indirecto a los objetos, pero difieren en sus capacidades y en sus consideraciones de seguridad. Los punteros ofrecen la flexibilidad de cambiar el objeto al que apuntan y se les puede asignar un valor nulo. Sin embargo, esta flexibilidad introduce riesgos, como desreferenciar punteros nulos o crear punteros colgantes. Las referencias, por otro lado, no pueden ser nulas y quedan vinculadas a objetos válidos desde su creación, lo que evita estos riesgos. Dado que son más seguras, conviene preferir las referencias sobre los punteros, a menos que se necesiten las funcionalidades adicionales que ofrecen los punteros.

Instrucciones

¡Te damos la bienvenida, ingeniero! Eres uno de los últimos veteranos de la Fundación Speedywagon, una organización secreta que lleva décadas combatiendo amenazas antiguas como los Pillar Men. En el transcurso de ese esfuerzo, has pasado años manteniendo los sistemas tecnológicos de la Fundación, construidos con una mezcla de tecnología de punta y bibliotecas antiguas.

Sin embargo, últimamente los sensores que rastrean la actividad de los Pillar Men están fallando. Los sistemas de la Fundación 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 principales que monitorean 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 encabezado para ti.

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

1. Verifica 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. Cuenta 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 en el 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 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 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 verificar si hay sensores nulos para evitar que el sistema falle.

  • Define una función que acepte el puntero a un pillar_men_sensor.
  • La función debe verificar 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. Revisa 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 se debe activar una alarma según los niveles de exposición a la luz UV y la actividad del sensor. La función uv_alarm debe usar la función uv_light_heuristic que se proporciona, la cual opera sobre un vector de datos y devuelve un valor según 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 que devuelve uv_light_heuristic es mayor que el nivel de sensor->activity; en caso contrario, devuelve false.
Editar en GitHub El enlace se abre en una ventana o una pestaña nuevas
C++ Exercism

¿Todo 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.