Hasta ahora hemos trabajado sobre todo con números y strings. Para modelar el mundo real, puede que queramos un número limitado de valores que una variable pueda tomar. Es posible que quieras un tipo específico con unos cuantos valores distintos, cada uno con un nombre distinto. Por ejemplo, en una fábrica de monopatines, que el material de la tabla solo pueda ser arce, bambú o plástico.
Podrías usar números enteros para codificar esos valores, pero tendrías que añadir código extra para comprobar si llega del sistema un valor no válido para el material.
El significado de esos números mágicos es difícil de seguir a lo largo del código fuente, y son propensos a confundirse.
Se pueden usar enumerations para fomentar un código expresivo y para limitar errores de comparación involuntarios.
El término específico para este tipo de enumeración es scoped enumeration.
El fragmento que aparece a continuación muestra cómo escribir una enumeration DeckMaterial.
Fíjate en la palabra clave enum class y en el ; al final de la definición:
enum class DeckMaterial {
maple,
bamboo,
plastic
};
Ahora, mira una función de precios en la tienda de monopatines y fíjate en el operador de resolución de scope (::), que especifica un enumerator de la enumeration:
double deck_price(double base_price, DeckMaterial material) {
if(material == DeckMaterial::plastic) {
return base_price * 0.9;
}
return base_price * 1.3;
}
Imagina que tienes una segunda enumeration para el material de las ruedas:
enum class WheelMaterial {
steel,
clay,
plastic
};
Aunque tanto las ruedas como la tabla pueden estar hechas de plástico, no se pueden confundir entre sí.
Son tipos distintos: DeckMaterial plástico y WheelMaterial plástico.
Cada enumeration tendrá sus enumerators en su propio scope, su propio namespace.
Esta es la razón por la que se llaman scoped enumerations.
Puede que estés pensando que, con un nombre como scoped, también habría enumeraciones unscoped, y estarías en lo cierto.
Las Unscoped enumerations son cada vez menos populares porque todas comparten el mismo namespace global.
Debido a esa compartición, no podrías tener dos unscoped enumerations con los mismos enumerators, como plastic, en el ejemplo anterior.
Además, las unscoped enumerations se convierten implícitamente a números enteros.
Mira el ejemplo siguiente para ver un resultado sorprendente:
enum CitrusFruits {
lemons, // 0
oranges, //1
};
enum IceCream {
walnut, // 0
apples, // 1
};
bool comparison{apples == oranges};
// => true
// Example from above:
bool comparison{DeckMaterial::plastic == WheelMaterial::plastic};
// => Does not compile!
Si quieres convertir scoped enumerations a números enteros, puedes usar static_cast<int>.
Como otros lenguajes, C++ también ofrece una instrucción switch.
Las instrucciones switch son una forma más corta de escribir largas instrucciones if ... else if.
Para crear un switch, empezamos usando la palabra clave switch seguida de un número entero.
Después declaramos cada una de las condiciones con la palabra clave case.
También podemos declarar un caso default, que se ejecutará cuando ninguna de las condiciones case anteriores coincida.
Cada caso debe terminar con una instrucción break (o return).
int price{0};
int adults{3};
int kids{2};
switch (int group_size{adults + kids}) {
case 1:
price = 50;
break;
case 2:
price = 70;
break;
default:
price = group_size * 30;
}
Una cosa importante sobre la construcción switch es que el código seguirá ejecutándose hasta que lo detenga una instrucción break (o return).
Esto puede provocar un comportamiento inesperado.
int adults{1};
int kids{0};
switch (int group_size{adults + kids}) {
case 1:
price = 50;
case 2:
price = 70;
default:
price = group_size * 30;
}
// price will be 30!
El principal caso de uso de esta ejecución continuada es una instrucción que tiene varias etiquetas. Varios resultados del switch pueden corresponder al mismo fragmento de código a ejecutar. De este modo, por ejemplo en una aplicación de reservas, la función llamada para los tamaños de grupo 2 y 3 puede ser la misma:
switch (group_size) {
case 1:
book_room();
break;
case 2:
case 3:
book_apartment(group_size);
break;
default:
book_house(group_size);
}
// book_apartment happens when group_size is 2 or 3
Tu amiga Helma ha creado un pequeño juego en línea que se ha vuelto muy popular rápidamente. Se llama HellMath. La pequeña comunidad ha atraído a algunos trolls que hacen que el juego y los foros sean bastante desagradables. Helma te ha pedido que trabajes en un nuevo sistema de permisos para separar a los alborotadores.
El foro admite tres acciones distintas:
Hay cuatro tipos de cuentas, cada uno con permisos predeterminados distintos:
Helma se ha dado cuenta de que no sirve de nada banear las cuentas de trolls. Su estrategia consiste en darles la ilusión de que su tiempo está «bien invertido», pero sus publicaciones solo se muestran a otros trolls. Para todo lo que requiera un orden de prioridad, los trolls van al final de cualquier secuencia. Cuando entran en una partida, el grupo de jugadores disponibles también se limita a otros trolls.
Esta práctica se llama baneo en la sombra.
En primer lugar, define una enumeración AccountStatus que represente los cuatro tipos de cuenta: troll, guest, user y mod.
A continuación, define una enumeración Action que represente los tres tipos de permiso: read, write y remove.
Cada publicación de los foros guarda el AccountStatus del autor en sus metadatos.
Asegúrate de que las publicaciones de los trolls solo se muestren a otros trolls.
Helma necesita una función display_post que recibe dos argumentos de tipo AccountStatus y devuelve un bool.
El primer argumento es el estado del autor de la publicación y el segundo, el estado de quien la ve.
using namespace hellmath;
display_post(AccountStatus::troll, AccountStatus::user);
// => false
display_post(AccountStatus::mod, AccountStatus::guest);
// => true
Helma necesita una forma de comprobar si una acción determinada está permitida para un usuario.
Implementa una función permission_check que reciba una Action como primer argumento y un AccountStatus con el que comprobar.
Debe devolver un bool de acuerdo con los permisos indicados en la introducción.
permission_check(Action::remove, AccountStatus::guest);
// => false
permission_check(Action::write, AccountStatus::mod);
// => true
Para que los jugadores reales del juego respondan de sus actos, Hellmath deniega el acceso a los usuarios invitados. Como se ha mencionado antes, Helma quiere que los trolls solo se encuentren con otros trolls. Las conexiones de juego entre los demás usuarios no tienen restricciones.
Implementa la función valid_player_combination que comprueba si dos jugadores pueden unirse a la misma partida.
La función tiene dos parámetros de tipo AccountStatus y devuelve un bool.
valid_player_combination(AccountStatus::guest, AccountStatus::mod);
// => false
valid_player_combination(AccountStatus::troll, AccountStatus::troll);
// => true
Con el enorme crecimiento del juego y de los foros, Helma ahora tiene que repartir la capacidad de cómputo y el ancho de banda entre los usuarios. Para gestionar las emergencias, los moderadores reciben la máxima prioridad. Los invitados se ponen en cola detrás de los usuarios normales, y los trolls se ordenan detrás de todos los demás.
Implementa la función has_priority que recibe dos argumentos de tipo AccountStatus y devuelve true si, y solo si, la primera cuenta tiene una prioridad estrictamente mayor que la segunda.
has_priority(AccountStatus::guest, AccountStatus::mod);
// => false
has_priority(AccountStatus::user, AccountStatus::troll);
// => true
Regístrate en Exercism para aprender y dominar C++ con 19 conceptos100 ejercicios y mentoría humana real, todo gratis.