Ya has usado y escrito funciones de C++ en un par de conceptos. Es hora de ponerse técnicos. El siguiente fragmento de código muestra los términos más comunes para que los tengas a mano. Como C++ ignora los espacios en blanco, se cambió el formato para poner cada elemento en una sola línea.
// 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
La declaración funciona como una nota para el compilador: le indica que existe una función con ese nombre, ese tipo de retorno y esa lista de parámetros. El código no funcionará si falta la definición. Las declaraciones son opcionales; se necesitan si usas la función antes de su definición. Las declaraciones pueden resolver problemas como las referencias cíclicas y se pueden usar para separar la interfaz de la implementación.
A veces quieres asegurarte de que los valores no se puedan cambiar después de inicializarlos.
C++ usa la palabra clave const como calificador para las constantes.
const int number_of_dragon_balls{7};
number_of_dragon_balls--; // compilation error
A menudo verás constantes escritas en UPPER_SNAKE_CASE. Se recomienda reservar este formato para las macros, si no hay otra convención.
Si intentas cambiar una variable constante después de haberla asignado, tu código no se compilará. Esto ayuda a evitar cambios no deseados, pero también abre posibilidades de optimización para el compilador. Además, para una persona es más fácil razonar sobre el código si sabe que ciertas partes no se verán afectadas.
También puedes usar const como calificador para los parámetros de una función.
string guess_number(const int& secret, const int& guess) {
if (secret < guess) return "lower.";
if (secret > guess) return "higher.";
return "exact!";
}
Cuando pasas una referencia const a la función, puedes tener la certeza de que se dejará sin cambios.
A menudo verás referencias const para objetos que podrían ser costosos de copiar, como strings más largos.
Un tercer caso de uso del calificador const son las funciones miembro que no cambian la instancia de una clase.
class Stubborn {
public:
Stubborn(string reply) {
response = reply;
}
string answer(const string& question) const {
if (question.length() == 0) { return ""; }
return response;
}
private:
string response{};
};
La función miembro answer de Stubborn usa una referencia const string& como parámetro.
Esto evita una operación de copia del objeto original que se pasó a la función.
Varias funciones pueden tener el mismo nombre si su lista de parámetros es diferente. Eso se llama sobrecarga de funciones y normalmente se hace cuando estas funciones realizan tareas muy similares.
El encabezado de la función sin el tipo de retorno es la firma de tipo de la función. Un cambio en la firma de tipo da como resultado una función nueva.
El ejemplo de play_sound tiene seis sobrecargas diferentes para cubrir distintos escenarios:
// 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);
La firma de tipo se define por el nombre de la función, el número de parámetros, sus tipos y sus calificadores (pero no sus nombres). El tipo de retorno explícitamente no forma parte de la firma de tipo, y obtendrás errores de compilación si tienes dos funciones que solo difieren en su tipo de retorno. El compilador se quejará porque no queda claro cuál de las dos se debe usar.
Algunas funciones pueden volverse muy largas, y muchas de sus llamadas podrían usar los mismos valores para la mayoría de los parámetros. La repetición en esas llamadas se puede evitar con argumentos predeterminados.
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 "Alruccabam", sire "Poseidon"
record_new_horse_birth("Highclere", 175, "off-white", "Fall Aspen"); // sire will be "Poseidon"
Como la declaración de la función a menudo se lee antes que la definición, es el mejor lugar para establecer los argumentos predeterminados. Si un parámetro tiene una declaración predeterminada, todos los parámetros a su derecha también necesitan una. A veces las sobrecargas de funciones complicadas se pueden refactorizar en menos funciones con argumentos predeterminados para mejorar la mantenibilidad.
En este ejercicio vas a escribir un poco más de código relacionado con la preparación y la cocción de tu brillante lasaña de tu libro de cocina favorito.
Tienes cinco tareas. La primera está relacionada con la cocción en sí, y las otras cuatro con la preparación perfecta.
Para la próxima lasaña que prepares, quieres asegurarte de reservar suficiente tiempo para poder disfrutar de la cocción. Ya planeaste qué capas tendrá tu lasaña. Ahora quieres estimar cuánto tardará la preparación con base en eso.
Implementa una función preparationTime que acepte un vector de capas como string y el tiempo de preparación promedio por capa en minutos como un int.
La función debe devolver la estimación del tiempo total de preparación con base en el número de capas, como un int.
Si no se pasa el tiempo de preparación promedio, se debe usar el valor predeterminado de 2.
std::vector<std::string> layers{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"};
preparationTime(layers, 3)
// => 18
preparationTime(layers)
// => 12
Además de reservar el tiempo, también quieres asegurarte de tener suficiente salsa y fideos para cocinar la lasaña de tus sueños. Por cada capa de fideos en tu lasaña, necesitarás 50 gramos de fideos. Por cada capa de salsa en tu lasaña, necesitarás 0.2 litros de salsa.
Define la función quantities que recibe un vector de capas como parámetro, de tipo string.
La función determinará entonces la cantidad de fideos y salsa necesarios para preparar tu comida.
El resultado debe devolverse como una struct amount, que ya está definida para ti.
amount tiene dos variables miembro: una para el peso de los noodles como un int y la segunda para el volumen de la sauce como un double.
struct amount { int noodles; double sauce; };
amount needed{quantities({"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"})};
// needed.noodles => 100
// needed.sauce => 0.4
Hace un tiempo visitaste a un amigo y comiste lasaña en su casa. Estaba increíble y tenía algo especial. Tu amigo te envió la lista de ingredientes y te dijo que el último elemento de la lista es el «ingrediente secreto» que hizo que la comida fuera tan especial. Ahora quieres agregar ese ingrediente secreto a tu receta también.
Escribe una función addSecretIngredient que acepte dos vector of strings como parámetros.
El primer parámetro es una referencia a los ingredientes de tu propia receta, y el segundo vector de strings tiene los ingredientes que te envió tu amigo.
El último elemento de tu lista de ingredientes siempre es "?".
La función debe reemplazarlo con el último elemento de la lista de tu amigo.
Nota: addSecretIngredient no devuelve nada; debes modificar la lista de tus ingredientes directamente.
La lista con los ingredientes de tu amigo también puede ser una referencia (para mejorar el rendimiento), pero no se debe modificar.
Asegúrate de que la lista no se modifique usando una referencia 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"}
Las cantidades que aparecen en tu libro de cocina solo alcanzan para dos porciones de lasaña. Como quieres cocinar para más personas la próxima vez, quieres calcular las cantidades para distintos números de porciones.
Implementa una función scaleRecipe que reciba dos parámetros.
double necesarias para 2 porciones.La función debe devolver un nuevo vector con las cantidades necesarias para el número de porciones deseado.
Sin embargo, quieres conservar la receta original.
Esto significa que el argumento quantities no se debe modificar en esta función.
std::vector<double> quantities{1.2, 3.6, 10.5};
scaleRecipe(quantities, 4);
// => { 2.4, 7.2, 21 }
Recuerdas historias de tu infancia, relatos de tu tía bisabuela, la guardiana de los secretos culinarios más profundos de la familia. Cuenta la leyenda que ella tiene la clave para desbloquear sabores más allá de lo imaginable, pero sus métodos no son convencionales. Ella no trabaja con listas ni medidas; habla en susurros y revela solo un ingrediente a la vez.
Con una mezcla de emoción y temor, decides contactar a tu tía bisabuela. Como era de esperar, responde a su manera enigmática, ofreciéndote un único ingrediente secreto, envuelto en misterio.
La contribución de tu tía bisabuela agrega una capa de complejidad que quieres usar en tu receta.
Implementa otra versión de la función addSecretIngredient que acepte dos argumentos: una referencia a un vector que representa tu lista de ingredientes y un solo string que representa la oferta enigmática de tu tía bisabuela.
std::vector<std::string> myList{"noodles", "meat", "sauce", "mozzarella", "?"};
std::string secretIngredient{"spice melange"};
addSecretIngredient(myList, secretIngredient);
// myList => {"noodles", "meat", "sauce", "mozzarella", "spice melange"}
¡Buena suerte, valiente chef!
Regístrate en Exercism para aprender y dominar C++ con 19 conceptos100 ejercicios y mentoría humana real, todo gratis.