Você já usou e escreveu funções em C++ em alguns conceitos. Agora é hora de entrar nos detalhes técnicos. O trecho de código abaixo mostra os termos mais comuns para consulta rápida. Como C++ ignora espaços em branco, a formatação foi alterada para colocar cada elemento em uma única linha.
// Function declaration:
bool // Return type
admin_detected(string user, string password) // Type signature
; // Don't forget the ';' for the declaration
// Function definition:
bool // Return type
admin_detected // Function name
(string user, string password) // Parameter list
{ return user == "admin" && password == "1234"; } // Function body
A declaração funciona como um aviso ao compilador de que existe uma função com aquele nome, tipo de retorno e lista de parâmetros. O código não vai funcionar se a definição estiver faltando. As declarações são opcionais: elas são necessárias se você usar a função antes da definição dela. As declarações podem resolver problemas como referências cíclicas e podem ser usadas para separar a interface da implementação.
const
Às vezes você quer garantir que valores não possam ser alterados depois de inicializados.
C++ usa a palavra-chave const como qualificador para constantes.
const int number_of_dragon_balls{7};
number_of_dragon_balls--; // compilation error
Você vai ver constantes escritas em UPPER_SNAKE_CASE com frequência. É recomendável reservar esse padrão de maiúsculas para macros, caso não haja outra convenção.
Se você tentar alterar uma variável constante depois de definida, seu código não vai compilar. Isso ajuda a evitar alterações indesejadas, mas também abre possibilidades de otimização para o compilador. Como pessoa, também fica mais fácil raciocinar sobre o código quando você sabe que certas partes não serão afetadas.
Você também pode usar const como qualificador para parâmetros de função.
string guess_number(const int& secret, const int& guess) {
if (secret < guess) return "lower.";
if (secret > guess) return "higher.";
return "exact!";
}
Quando você passa uma referência const para a função, pode ter certeza de que ela será deixada inalterada.
Você vai ver referências const com frequência para objetos que podem ser custosos de copiar, como strings mais longas.
Um terceiro caso de uso do qualificador const são funções membro que não alteram a instância de uma classe.
class Stubborn {
public:
Stubborn(string reply) {
response = reply;
}
string answer(const string& question) const {
if (question.length() == 0) { return ""; }
return response;
}
private:
string response{};
};
A função membro answer da classe Stubborn usa uma referência const string& como parâmetro.
Isso evita uma operação de cópia do objeto original que foi passado para a função.
Várias funções podem ter o mesmo nome se a lista de parâmetros for diferente. Isso se chama sobrecarga de funções e geralmente é feito quando essas funções realizam tarefas muito parecidas.
O cabeçalho da função sem o tipo de retorno é a assinatura de tipo da função. Uma mudança na assinatura de tipo resulta em uma nova função.
O exemplo de play_sound tem seis sobrecargas diferentes para acomodar cenários distintos:
// different argument types:
void play_sound(char note); // C, D, E, ..., B
void play_sound(string solfege); // do, re, mi, ..., ti
void play_sound(int jianpu); // 1, 2, 3, ..., 7
// different number of arguments:
void play_sound(string solfege, double duration);
// different qualifiers:
void play_sound(vector<string>& solfege);
void play_sound(const vector<string>& solfege);
A assinatura de tipo é definida pelo nome da função, pelo número de parâmetros, pelos tipos deles e pelos qualificadores deles (mas não pelos nomes). O tipo de retorno explicitamente não faz parte da assinatura de tipo, e você vai receber erros de compilação se tiver duas funções que diferem apenas no tipo de retorno. O compilador vai reclamar porque não fica claro qual das duas deve ser usada.
Algumas funções podem ficar bem longas, e muitas das chamadas delas podem usar os mesmos valores para a maioria dos parâmetros. A repetição nessas chamadas pode ser evitada com argumentos padrão.
void record_new_horse_birth(string name, int weight, string color="brown-ish", string dam="Alruccaba", string sire="Poseidon");
record_new_horse_birth("Urban Sea", 130); // color will be brown, dam "Alruccaba", sire "Poseidon"
record_new_horse_birth("Highclere", 175, "off-white", "Fall Aspen"); // sire will be "Poseidon"
Como a declaração da função costuma ser lida antes da definição, ela é o melhor lugar para definir os argumentos padrão. Se um parâmetro tem uma declaração padrão, todos os parâmetros à direita dele também precisam de uma declaração padrão. Às vezes, sobrecargas de funções complicadas podem ser refatoradas para menos funções com argumentos padrão, o que melhora a facilidade de manutenção.
Neste exercício, você vai escrever mais um pouco de código relacionado ao preparo e ao cozimento da sua lasanha maravilhosa, tirada do seu livro de receitas favorito.
Você tem cinco tarefas. A primeira tem a ver com o cozimento em si, e as outras quatro são sobre a preparação perfeita.
Para a próxima lasanha que você vai preparar, você quer garantir que reservou tempo suficiente para poder aproveitar o momento de cozinhar. Você já planejou quais camadas a sua lasanha vai ter. Agora você quer estimar quanto tempo a preparação vai levar com base nisso.
Implemente uma função preparationTime que aceita um vector de camadas como string e o tempo médio de preparação por camada, em minutos, como um int.
A função deve retornar a estimativa do tempo total de preparação com base no número de camadas, como um int.
Se o tempo médio de preparação não for informado, o valor padrão de 2 deve ser usado.
std::vector<std::string> layers{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"};
preparationTime(layers, 3)
// => 18
preparationTime(layers)
// => 12
Além de reservar o tempo, você também quer garantir que tem molho e massa suficientes para preparar a lasanha dos seus sonhos. Para cada camada de massa na sua lasanha, você vai precisar de 50 gramas de massa. Para cada camada de molho na sua lasanha, você vai precisar de 0,2 litros de molho.
Defina a função quantities, que recebe um vector de camadas como parâmetro, do tipo string.
A função então determina a quantidade de massa e molho necessária para fazer a sua refeição.
O resultado deve ser retornado como uma struct amount, que já está definida para você.
amount tem duas variáveis membro: uma para o peso da noodles, como um int, e a segunda para o volume do sauce, como um double.
struct amount { int noodles; double sauce; };
amount needed{quantities({"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"})};
// needed.noodles => 100
// needed.sauce => 0.4
Um tempo atrás, você visitou um amigo e comeu lasanha na casa dele. Estava incrível e tinha algo especial. O amigo enviou para você a lista de ingredientes e disse que o último item da lista é o "ingrediente secreto" que deixou a refeição tão especial. Agora você quer adicionar esse ingrediente secreto à sua receita também.
Escreva uma função addSecretIngredient que aceita dois vector of strings como parâmetros.
O primeiro parâmetro é uma referência para os ingredientes da sua própria receita, e o segundo vector de strings contém os ingredientes que o seu amigo enviou.
O último elemento da sua lista de ingredientes é sempre "?".
A função deve substituí-lo pelo último item da lista do seu amigo.
Observação: addSecretIngredient não retorna nada. Você deve modificar a lista dos seus ingredientes diretamente.
A lista com os ingredientes do seu amigo também pode ser uma referência (para melhorar o desempenho), mas ela não deve ser modificada.
Certifique-se de que a lista não será modificada, usando uma referência const.
std::vector<std::string> friendsList{"noodles", "sauce", "mozzarella", "kampot pepper"};
std::vector<std::string> myList{"noodles", "meat", "sauce", "mozzarella", "?"};
addSecretIngredient(myList, friendsList);
// myList => {"noodles", "meat", "sauce", "mozzarella", "kampot pepper"}
As quantidades listadas no seu livro de receitas rendem lasanha suficiente para apenas duas porções. Como você quer cozinhar para mais pessoas na próxima vez, você quer calcular as quantidades para diferentes números de porções.
Implemente uma função scaleRecipe que recebe dois parâmetros.
double, necessárias para 2 porções.A função deve retornar um novo vector com as quantidades necessárias para o número de porções desejado.
Ainda assim, você quer manter a receita original.
Isso significa que o argumento quantities não deve ser modificado nessa função.
std::vector<double> quantities{1.2, 3.6, 10.5};
scaleRecipe(quantities, 4);
// => { 2.4, 7.2, 21 }
Você se lembra de histórias da sua infância, contos sobre a sua tia-bisavó, a guardiã dos segredos culinários mais profundos da família. A lenda diz que ela tem a chave para desvendar sabores além da imaginação, mas os métodos dela são pouco convencionais. Ela não lida com listas nem medidas; ela fala em sussurros, revelando apenas um ingrediente de cada vez.
Com uma mistura de empolgação e apreensão, você decide entrar em contato com a sua tia-bisavó. Como esperado, ela responde do jeito enigmático dela, oferecendo um único ingrediente secreto, envolto em mistério.
A contribuição da sua tia-bisavó adiciona uma camada de complexidade que você quer usar na sua receita.
Implemente outra versão da função addSecretIngredient que aceita dois argumentos: uma referência a um vector que representa a sua lista de ingredientes e uma única string que representa a oferta enigmática da sua tia-bisavó.
std::vector<std::string> myList{"noodles", "meat", "sauce", "mozzarella", "?"};
std::string secretIngredient{"spice melange"};
addSecretIngredient(myList, secretIngredient);
// myList => {"noodles", "meat", "sauce", "mozzarella", "spice melange"}
Boa sorte, chef corajoso!
Crie sua conta no Exercism para aprender e dominar C++ com 19 conceitos100 exercícios e mentoria humana de verdade, tudo de graça.