Até agora, temos lidado sobretudo com números e strings. Para modelar o mundo real, podemos querer um número limitado de valores que uma variável pode assumir. Podes querer um tipo dedicado com alguns valores distintos, cada um com o seu nome. Por exemplo, numa fábrica de skates, o material da prancha pode ser apenas uma escolha entre ácer, bambu ou plástico.
Podias usar números inteiros para codificar esses valores, mas terias de escrever código adicional para verificar se está a chegar do sistema um valor inválido para o material.
O significado desses números mágicos é difícil de seguir ao longo do código-fonte, e são fáceis de confundir.
As enumerations podem ser usadas para incentivar código expressivo e para restringir erros de comparação involuntários.
O termo específico para este tipo de enumeração é scoped enumeration.
O excerto abaixo mostra como escrever uma enumeration DeckMaterial.
Repara na palavra-chave enum class e no ; no fim da definição:
enum class DeckMaterial {
maple,
bamboo,
plastic
};
Agora, olha para uma função de preços da loja de skates e repara no operador de resolução de âmbito (::) que especifica um enumerator da 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 tens uma segunda enumeration para o material das rodas:
enum class WheelMaterial {
steel,
clay,
plastic
};
Embora as rodas e a prancha possam ambas ser feitas de plástico, as duas não podem ser confundidas.
São tipos diferentes: DeckMaterial plastic e WheelMaterial plastic.
Cada enumeration terá os seus enumerators no seu próprio âmbito, o seu próprio namespace.
É por isso que se chamam scoped enumerations.
Podes estar a pensar que, com um nome como scoped, também deve haver enumerações unscoped, e tens razão.
As Unscoped enumerations estão a tornar-se menos populares porque partilham todas o mesmo namespace global.
Por causa dessa partilha, não podias ter duas unscoped enumerations com os mesmos enumerators, como plastic no exemplo acima.
Além disso, as unscoped enumerations convertem-se implicitamente em números inteiros.
Olha para o exemplo abaixo, que tem um resultado surpreendente:
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!
Se quiseres converter scoped enumerations em números inteiros, podes usar static_cast<int>.
Tal como outras linguagens, o C++ também disponibiliza uma instrução switch.
As instruções switch são uma forma mais curta de escrever longas instruções if ... else if.
Para criar um switch, começamos por usar a palavra-chave switch seguida de um número inteiro.
De seguida, declaramos cada uma das condições com a palavra-chave case.
Também podemos declarar um caso default, que será executado quando nenhuma das condições case anteriores corresponder.
Cada caso deve terminar com uma instrução break (ou um 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;
}
Uma coisa importante sobre a construção switch é que o código continua a executar-se até ser interrompido por uma instrução break (ou um return).
Isto pode levar a um comportamento 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!
O principal caso de uso desta continuação da execução é uma instrução com vários rótulos. Vários resultados do switch podem corresponder ao mesmo bloco de código a executar. Assim, por exemplo numa aplicação de reservas, a função chamada para grupos de tamanho 2 e 3 pode ser a mesma:
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
A tua amiga Helma criou um pequeno jogo online que ganhou popularidade muito depressa. Chama-se HellMath. A pequena comunidade atraiu alguns trolls que tornam o jogo e os fóruns bastante desagradáveis. A Helma pediu-te para trabalhares num novo sistema de permissões para separar quem causa problemas.
O fórum suporta três ações diferentes:
Existem quatro tipos de contas, cada um com permissões predefinidas diferentes:
A Helma reparou que não vale a pena banir contas de trolls. A estratégia dela é dar-lhes a ilusão de que o seu tempo está "bem investido", mas as suas publicações só são mostradas a outros trolls. Em tudo o que exija uma ordenação por prioridade, os trolls ficam em último lugar em qualquer sequência. Quando entram num jogo, o conjunto de jogadores disponíveis também fica limitado a outros trolls.
Esta prática chama-se shadow-banning.
Primeiro, define uma enumeração AccountStatus para representar os quatro tipos de conta: troll, guest, user e mod.
De seguida, define uma enumeração Action para representar os três tipos de permissão: read, write e remove.
Todas as publicações nos fóruns guardam o AccountStatus de quem publicou nos seus metadados.
Certifica-te de que as publicações dos trolls só são mostradas a outros trolls.
A Helma precisa de uma função display_post, que recebe dois argumentos do tipo AccountStatus e devolve um bool.
O primeiro argumento é o estado de quem publicou e o segundo é o estado de quem está a ver.
using namespace hellmath;
display_post(AccountStatus::troll, AccountStatus::user);
// => false
display_post(AccountStatus::mod, AccountStatus::guest);
// => true
A Helma precisa de uma forma de verificar se uma determinada ação é permitida a um utilizador.
Implementa uma função permission_check, que recebe uma Action como primeiro argumento e um AccountStatus para verificar.
Deve devolver um bool de acordo com as permissões indicadas na introdução.
permission_check(Action::remove, AccountStatus::guest);
// => false
permission_check(Action::write, AccountStatus::mod);
// => true
Para manter os jogadores reais responsáveis pelas suas ações, a Hellmath nega o acesso aos utilizadores convidados. Como já foi referido, a Helma quer que os trolls só incomodem outros trolls. As ligações de jogo entre os outros utilizadores não têm restrições.
Implementa a função valid_player_combination, que verifica se dois jogadores podem entrar no mesmo jogo.
A função tem dois parâmetros do tipo AccountStatus e devolve um bool.
valid_player_combination(AccountStatus::guest, AccountStatus::mod);
// => false
valid_player_combination(AccountStatus::troll, AccountStatus::troll);
// => true
Com o enorme crescimento do jogo e dos fóruns, a Helma tem agora de distribuir capacidade de processamento e largura de banda pelos utilizadores. Para lidar com emergências, os moderadores recebem a prioridade mais alta. Os convidados ficam atrás dos utilizadores normais na fila, e os trolls ficam atrás de todos os outros.
Implementa a função has_priority, que recebe dois argumentos do tipo AccountStatus e devolve true se, e só se, a primeira conta tiver uma prioridade estritamente superior à da segunda.
has_priority(AccountStatus::guest, AccountStatus::mod);
// => false
has_priority(AccountStatus::user, AccountStatus::troll);
// => true
Inscreve-te no Exercism para aprenderes e dominares C++ com 19 conceitos100 exercícios, e mentoria humana real, tudo grátis.