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:
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.
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);
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.
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;
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.
¡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.
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{};
};
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.
pillar_men_sensor.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
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.
pillar_men_sensor sensor_array[3] = {{0}, {101}, {22}};
int totalActivity = activity_counter(sensor_array, 3);
// totalActivity => 123
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.
pillar_men_sensor.nullptr. Si el sensor es nullptr, devuelve false.true; en caso contrario, devuelve false.pillar_men_sensor db{9008, "songokunoie", {7, 7, 7}};
bool alarm = alarm_control(&db);
// alarm => true
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:
pillar_men_sensor.false si el puntero del sensor es nulo.uv_light_heuristic, pasando la dirección del array data del sensor.true si el valor devuelto por uv_light_heuristic es mayor que el nivel sensor->activity; en caso contrario, devuelve false.Regístrate en Exercism para aprender y dominar C++ con 19 conceptos100 ejercicios y mentoría humana real, todo gratis.