Rutas
/
C++
C++
/
Ejercicios
/
El poder de Troy
El poder de Troy

El poder de Troy

Ejercicio de aprendizaje

Introducción

La palabra clave auto en C++

En C++, la palabra clave auto es una funcionalidad muy potente introducida en C++11 que sirve para declarar variables con un tipo de dato inferido. El compilador deduce el tipo de la variable a partir de su inicializador, lo que puede hacer que el código sea más legible y fácil de mantener.

Ejemplo de uso

Fíjate en el siguiente ejemplo, en el que se usa auto para declarar variables:

auto dragon_population{3};          // dragon_population is deduced as an integer
auto westeros{7.7777};              // westeros is deduced as a double
auto wedding_location{"The Twins"}; // wedding_location is deduced as a const char*, not std::string

En cada caso, el tipo de la variable se infiere del valor con el que se inicializa.

Inferencia de tipos

La palabra clave auto ayuda a escribir código más conciso y legible al reducir la verbosidad de los tipos explícitos.

const std::vector<std::string> pigeon_pie{"flour", "butter", "pigeon", "salt"};
auto purple_wedding_pie{pigeon_pie};
purple_wedding_pie.emplace_back("the strangler");

En este fragmento de código, auto deduce el tipo de purple_wedding_pie como std::vector<std::string>, lo que evita tener que especificar el tipo explícitamente otra vez.

Compatibilidad

La palabra clave auto es compatible con diversas construcciones de C++, lo que la convierte en una herramienta versátil en la programación moderna en C++.

auto& element{array[0]};           // reference to an element
const auto object{otherObject};    // const type version of otherObject's type
auto* ptr{&x};                     // pointer to x with the same type as x, but as a pointer.

En conceptos posteriores veremos a menudo la palabra clave auto con expresiones lambda, bucles for basados en rangos e iteradores.

Punteros inteligentes

Los punteros inteligentes son una funcionalidad moderna de C++ diseñada para proporcionar una gestión automática de la memoria, lo que ayuda a evitar las fugas de memoria y los punteros colgantes que suelen asociarse a los punteros sin procesar. Actúan como envoltorios de los punteros sin procesar y añaden funcionalidad adicional, como la liberación automática de memoria cuando el puntero ya no es necesario.

Sintaxis general

Los punteros inteligentes se implementan normalmente como plantillas de clase en la biblioteca estándar de C++. Los dos punteros inteligentes más utilizados son std::unique_ptr y std::shared_ptr.

Punteros únicos

std::unique_ptr es un puntero inteligente que posee el objeto en exclusiva. Garantiza que, en un momento dado, solo un objeto std::unique_ptr posea el recurso. Cuando el std::unique_ptr propietario se destruye o se reinicia, destruye automáticamente los objetos y libera su memoria.

#include <memory>
// Declaring and defining a unique pointer
auto rightful_king_of_england = std::make_unique<std::string>("Excalibur");

// Unique pointers cannot be copied or assigned
auto mordred = rightful_king_of_england; // Error: Cannot copy a unique_ptr

Ventajas de std::make_unique()

Al crear un std::unique_ptr, es preferible usar std::make_unique() en lugar de usar new directamente para reservar memoria. std::make_unique() ofrece varias ventajas:

  1. Seguridad ante excepciones: std::make_unique() garantiza la seguridad ante excepciones. Si se lanza una excepción durante la construcción del objeto, la memoria se liberará automáticamente, lo que evita fugas de memoria.
  2. Claridad: usar std::make_unique() hace que el código sea más claro y conciso. Elimina la necesidad de especificar explícitamente el tipo que se está reservando, ya que los argumentos de plantilla se deducen automáticamente.
  3. Oportunidades de optimización: los compiladores pueden optimizar std::make_unique() con más eficacia que una reserva de memoria manual con new, lo que puede traducirse en una mejora del rendimiento.
  4. Evitar un uso indebido: es posible eliminar el recurso subyacente cuando el std::unique_ptr se construye de forma manual. Eso daría lugar a un comportamiento indefinido cuando el std::unique_ptr intente eliminarlo al final de su scope.

Punteros compartidos

std::shared_ptr es un puntero inteligente que permite que varios objetos std::shared_ptr compartan la propiedad del mismo recurso. Lleva la cuenta de cuántos punteros compartidos hacen referencia al recurso y solo libera la memoria cuando el último puntero compartido que posee el recurso sale de su scope o se reinicia.

// Declaring and defining a shared pointer to a dynamically allocated string
auto martian_congressional_republic = std::make_shared<std::string>("protomolecule");

// Creating more shared pointer that shares ownership
auto outer_planets_alliance = martian_congressional_republic;
auto united_nations = martian_congressional_republic;
Caution

En C++17 y versiones anteriores, no se admite directamente el uso de std::shared_ptr con arrays mediante std::make_shared<T[]>. Aunque es posible reservar arrays con std::make_shared<T[]>, crear punteros compartidos directamente a partir de ellos puede dar lugar a un comportamiento indefinido debido a las diferencias en la gestión de memoria entre objetos individuales y arrays. En su lugar, plantéate usar std::vector o funciones de eliminación personalizadas para gestionar arrays con punteros compartidos de forma eficaz. Asegúrate siempre de la compatibilidad con tu compilador y con tu implementación de la biblioteca estándar cuando trabajes con reservas de arrays y punteros compartidos en C++17.

Ventajas de std::make_shared()

Al igual que std::make_unique(), std::make_shared() ofrece ventajas como una mayor eficiencia de memoria, seguridad ante excepciones y legibilidad. Combina la reserva de memoria del bloque de control y del objeto gestionado en una sola operación, lo que mejora la eficiencia y reduce el riesgo de fugas de memoria. Además, la deducción automática de los argumentos de plantilla simplifica el código y mejora la legibilidad. Usar std::make_shared() favorece un código más limpio, seguro y eficiente cuando trabajas con objetos std::shared_ptr en C++.

Consejos de uso

Usa punteros inteligentes de forma predeterminada: std::unique_ptr para la propiedad exclusiva y std::shared_ptr para la propiedad compartida. Reserva los punteros sin procesar para referencias que no impliquen propiedad o cuando tengas que interactuar con código heredado. En la mayoría de los casos, std::unique_ptr basta para la propiedad exclusiva, ya que ofrece una gestión de memoria ligera sin la sobrecarga del recuento de referencias. std::shared_ptr debe usarse con moderación, ya que introduce sobrecarga y complejidad a menos que se necesite una propiedad compartida real.

Instrucciones

Este ejercicio te lleva al mundo de Troya. La vida de sus gentes está llena de maravillas y magia. Muchos humanos de Troya poseen poderes, que usan con frecuencia en su día a día. Los poderes sirven para remodelar el mundo o para influir en la fauna de Troya y en otras personas. La magia también se manifiesta en artefactos únicos, muy codiciados por aventureros, artesanos y sabios.

En este ejercicio vas a escribir código para modelar a los humanos de Troya, los artefactos que poseen y las interacciones entre poderes.

Tienes seis tareas. La primera está relacionada con crear un humano; las otras cinco tratan sobre el manejo de poderes y artefactos.

1. Trae a los humanos al mundo de Troya

Para tu modelo de Troya, los humanos son lo más importante. El humano de tu modelo debería poder poseer un artefacto único. También debería tener la capacidad de manifestar un poder. Estos poderes pueden afectar a otros humanos, así que también querrás modelar si un humano está influido por algún otro poder.

Se te proporcionan implementaciones básicas de los structs artifact y power. Implementa un struct (o clase) human que tenga un puntero inteligente a una variable miembro artifact llamada possession. Cada artefacto solo puede ser poseído por un único humano en un momento dado.

Un human debe tener dos variables miembro adicionales. Una guarda su own_power y la otra es un poder por el que están influenced_by. Tanto own_power como influenced_by son punteros inteligentes a powers. Cada power puede ser propiedad de un único humano, pero también puede influir en otros humanos al mismo tiempo.

De forma predeterminada, los humanos nacen sin ningún artefacto, no poseen ningún poder y tampoco están influidos por ellos.

human mindy_mccready{};
mindy_mccready.possession;
// => nullptr
mindy_mccready.own_power;
// => nullptr
mindy_mccready.influenced_by;
// => nullptr

2. Trae los artefactos a Troya

Tu modelo es aburrido sin la interacción de sus partes. Quieres crear artefactos únicos y dárselos a determinados humanos.

Define la función give_new_artifact, que no devuelve nada, pero recibe una referencia a un human y un string. Con el string debe definir un nuevo objeto artifact y establecer el puntero possession del human en consecuencia. La función no debe devolver nada.

human erik_magnus_lehnsherr{};
give_new_artifact(erik_magnus_lehnsherr, "Mind shielding helmet");

erik_magnus_lehnsherr.possession->name;
// "Mind shielding helmet"

3. Haz que los objetos se puedan intercambiar

El mundo de Troya gira en torno a la interacción. Quieres que la gente haga trueques intercambiando sus posesiones.

Escribe una función exchange_artifacts, que no devuelve nada, pero recibe dos punteros inteligentes a artefactos para intercambiar los objetos. Recuerda que no puedes copiar un unique_ptr. Esto incluye su uso en los parámetros de la función. En su lugar, usa una referencia al unique_ptr.

human uchiha{};
give_new_artifact(uchiha, "konoha headband");
human uzumaki{};
give_new_artifact(uzumaki, "forehead protector");

exchange_artifacts(uchiha.possession, uzumaki.possession);

uchiha.possession->name;
// "forehead protector"
uzumaki.possession->name;
// "konoha headband"

4. Da poder al pueblo

Lo más emocionante de Troya son los poderes especiales que la gente puede llegar a dominar. Algunos pueden fundir hierro con el pensamiento, mientras que otros pueden curar cualquier herida al instante durante la noche.

Define la función manifest_power, que no devuelve nada, pero recibe una referencia a un human y un string. Con el string debe definir un nuevo objeto power y establecer el puntero own_power del human en consecuencia. La función no debe devolver nada.

human eleven {};
manifest_power(eleven, "psychokinesis");

eleven.own_power->effect;
// "psychokinesis"

5. Usa el poder

¿De qué sirven los poderes más grandes si no puedes usarlos? Tu modelo se centra en los humanos, así que quieres hacer un seguimiento de la influencia de los poderes.

Escribe una función void use_power que reciba dos referencias a humanos. El primer humano es quien lanza el poder y el segundo representa el objetivo. El puntero influenced_by del objetivo debe apuntar al poder de quien lo lanza.

Por simplificar, los humanos solo pueden estar influidos por un único poder. Este poder permanece en su sitio aunque quien lo lanzó ya no exista.

human pamela_isley{};
manifest_power(pamela_isley, "control pheromones");

human count_vertigo{};
use_power(pamela_isley, count_vertigo);
count_vertigo.influenced_by->effect;
// "control pheromones"

6. Vigila la intensidad del poder

Ciertos poderes pierden potencia o desencadenan ciertos efectos en tu simulación cuando se aplican a varios humanos. Quieres hacer un seguimiento del número de personas conectadas a cada poder.

Define la función power_intensity, que recibe un humano y devuelve la intensidad de su poder como un int. Si la persona no tiene poder, el valor devuelto debe ser 0. En caso contrario, la intensidad debe reflejar a quien lanza el poder y a todas las personas influidas actualmente.

human jean_grey{};
manifest_power(jean_grey, "uplifting personality");

human scott{};
human logan{};
human ororo{};

use_power(jean_grey, ororo);
use_power(jean_grey, logan);
use_power(jean_grey, scott);

power_intensity(jean_grey);
// 4
Editar en GitHub El enlace se abre en una ventana o pestaña nueva
C++ Exercism

¿Listo para empezar El poder de Troy?

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