auto en C++En C++, la palabra clave auto es una funcionalidad poderosa que se introdujo en C++11 y 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 más fácil de mantener.
Fíjate en el siguiente ejemplo, donde 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 a partir del valor con el que se inicializa.
La palabra clave auto te ayuda a escribir código más conciso y legible, ya que reduce 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 bucle, auto deduce que el tipo de purple_wedding_pie es std::vector<std::string>, lo que evita tener que especificar el tipo otra vez.
La palabra clave auto es compatible con varias 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 junto con expresiones lambda, bucles for basados en rangos e iteradores.
Los punteros inteligentes son una funcionalidad moderna de C++ diseñada para ofrecer gestión automática de memoria, y ayudan a evitar las fugas de memoria y los punteros colgantes que suelen acompañar a los punteros crudos. Actúan como wrappers alrededor de los punteros crudos y añaden funcionalidad adicional, como liberar la memoria automáticamente cuando el puntero ya no se necesita.
Los punteros inteligentes suelen implementarse como plantillas de clase en la biblioteca estándar de C++.
Los dos punteros inteligentes más usados son std::unique_ptr y std::shared_ptr.
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 los objetos automáticamente 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
std::make_unique()
Cuando creas un std::unique_ptr, es preferible usar std::make_unique() en lugar de usar new directamente para asignar memoria.
std::make_unique() ofrece varias ventajas:
std::make_unique() garantiza la seguridad ante excepciones.
Si se lanza una excepción durante la construcción del objeto, la memoria se libera automáticamente, lo que evita fugas de memoria.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 asigna, ya que los argumentos de plantilla se deducen automáticamente.std::make_unique() de forma más eficaz que si asignas la memoria manualmente con new, lo que puede mejorar el rendimiento.std::unique_ptr se construye manualmente.
Eso provocaría un comportamiento indefinido cuando el std::unique_ptr intenta eliminarlo al final de su ámbito.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 ámbito 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;
En C++17 y versiones anteriores, no se admite directamente usar std::shared_ptr con arrays mediante std::make_shared<T[]>.
Aunque es posible asignar arrays con std::make_shared<T[]>, crear punteros compartidos directamente a partir de ellos puede provocar un comportamiento indefinido debido a las diferencias en la gestión de memoria entre objetos individuales y arrays.
En su lugar, considera 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 la implementación de la biblioteca estándar cuando trabajes con asignaciones de arrays y punteros compartidos en C++17.
std::make_shared()
De forma similar a std::make_unique(), std::make_shared() ofrece beneficios como una mejor eficiencia de memoria, seguridad ante excepciones y legibilidad.
Combina la asignación 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 su legibilidad.
Usar std::make_shared() favorece un código más limpio, más seguro y más eficiente cuando trabajas con objetos std::shared_ptr en C++.
Usa punteros inteligentes de forma predeterminada: std::unique_ptr para la propiedad exclusiva y std::shared_ptr para la propiedad compartida.
Reserva los punteros crudos para referencias que no implican propiedad o cuando interactúes 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 conteo de referencias.
std::shared_ptr debería usarse con moderación, ya que introduce sobrecarga y complejidad a menos que se necesite una propiedad compartida de verdad.
Este ejercicio te lleva al mundo de Troya. La vida de su gente está llena de asombro y magia. Muchos humanos en Troya poseen poderes, que usan con frecuencia en su vida diaria. Los poderes se usan para remodelar el mundo o 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 cómo manejar poderes y artefactos.
Para tu modelo de Troya, los humanos son la característica más importante. Tu humano de modelo debe poder poseer un artefacto único. También debe tener la capacidad de manifestar un poder. Estos poderes pueden afectar a otros humanos, así que también quieres modelar si un humano está influenciado por algún otro poder.
Se te proporcionan implementaciones básicas de los structs artifact y power.
Implementa un struct human (o clase) que tenga un puntero inteligente a una variable miembro de tipo 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 contiene su own_power y la otra es el poder por el que está influenciado (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.
Por defecto, los humanos nacen sin ningún artefacto y no poseen ningún poder ni están influenciados por ninguno.
human mindy_mccready{};
mindy_mccready.possession;
// => nullptr
mindy_mccready.own_power;
// => nullptr
mindy_mccready.influenced_by;
// => nullptr
Tu modelo es aburrido sin la interacción de sus partes. Quieres crear artefactos únicos y dárselos a ciertos 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"
El mundo de Troya gira en torno a la interacción. Quieres que las personas hagan trueques intercambiando sus posesiones.
Escribe una función exchange_artifacts, que no devuelve nada pero recibe dos punteros inteligentes de 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"
La característica más emocionante de Troya son los poderes especiales que las personas pueden ejercer. Algunos pueden fundir hierro con sus pensamientos, mientras que otros pueden sanar 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"
¿De qué sirven los poderes más grandes si no puedes usarlos? Tu modelo se centra en los humanos, así que quieres rastrear la influencia de los poderes.
Escribe una función void use_power que recibe dos referencias a humanos.
El primer humano es el lanzador y el segundo representa al objetivo.
El puntero influenced_by del objetivo debe apuntar al poder del lanzador.
Para simplificar, los humanos solo pueden estar influenciados por un único poder. Este poder permanece en su lugar incluso si el lanzador ya no existe.
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"
Ciertos poderes pierden su potencia o desencadenan ciertos efectos en tu simulación cuando se aplican a varios humanos. Quieres rastrear el número de personas que están 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.
De lo contrario, la intensidad debe reflejar al lanzador y a todas las personas influenciadas 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
Regístrate en Exercism para aprender y dominar C++ con 19 conceptos100 ejercicios y mentoría humana real, todo gratis.