Ya has usado y escrito funciones de C++ en un par de conceptos. Es hora de ponerse técnico. El fragmento de código que aparece a continuación muestra los términos más comunes para consultarlos fácilmente. Como C++ ignora los espacios en blanco, se ha modificado 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: que existe una función con ese nombre, ese tipo devuelto y esa lista de parámetros. El código no funcionará si falta la definición. Las declaraciones son opcionales; son necesarias 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.
const
A veces quieres asegurarte de que los valores no se puedan cambiar una vez inicializados.
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 las constantes escritas en UPPER_SNAKE_CASE. Se recomienda reservar este uso de mayúsculas para las macros, si no hay otra convención.
Si intentas cambiar una variable constante una vez establecida, tu código no compilará. Esto ayuda a evitar cambios accidentales, pero también abre posibilidades de optimización para el compilador. Como persona, también te resulta más fácil razonar sobre el código si sabes 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 estar seguro 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 modifican 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 suele hacerse cuando estas funciones realizan tareas muy similares.
La cabecera de la función sin el tipo devuelto es la signatura de tipo de la función. Un cambio en la signatura de tipo da lugar a 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 signatura 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 devuelto no forma parte de la signatura de tipo de forma explícita, y obtendrás errores de compilación si tienes dos funciones que solo se diferencian en el tipo devuelto. El compilador se quejará porque no queda claro cuál de las dos debería usarse.
Algunas funciones pueden volverse muy largas y muchas de sus llamadas pueden usar los mismos valores para la mayoría de los parámetros. La repetición en esas llamadas se puede evitar con argumentos por defecto.
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 suele leerse antes que la definición, es el mejor lugar para establecer los argumentos por defecto. Si un parámetro tiene una declaración por defecto, todos los parámetros a su derecha también necesitan una declaración por defecto. A veces, las sobrecargas de funciones complicadas se pueden refactorizar a menos funciones con argumentos por defecto para mejorar el mantenimiento.
En este ejercicio vas a escribir un poco más de código relacionado con la preparación y el cocinado de tu magnífica lasaña de tu libro de cocina favorito.
Tienes cinco tareas. La primera está relacionada con el cocinado 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 del cocinado. Ya has planificado qué capas tendrá tu lasaña. Ahora quieres estimar cuánto durará la preparación en función de eso.
Implementa una función preparationTime que acepta un vector de capas como string y el tiempo medio de preparación por capa en minutos como un int.
La función debe devolver la estimación del tiempo total de preparación en función del número de capas como un int.
Si no se pasa el tiempo medio de preparación, se debe usar el valor predeterminado 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 de tu lasaña, necesitarás 50 gramos de fideos. Por cada capa de salsa de tu lasaña, necesitarás 0,2 litros de salsa.
Define la función quantities que toma como parámetro un vector de capas como string.
La función determinará entonces la cantidad de fideos y salsa necesarios para preparar tu comida.
El resultado debe devolverse como un struct amount, que ya está definido 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. El 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 añadir también ese ingrediente secreto a tu receta.
Escribe una función addSecretIngredient que acepta 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 por 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 debe modificarse.
Asegúrate de que la lista no se modifique con 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 indicadas en tu libro de cocina solo alcanzan para dos porciones de lasaña. Como la próxima vez quieres cocinar para más gente, quieres calcular las cantidades para diferentes números de porciones.
Implementa una función scaleRecipe que toma dos parámetros.
double necesarias para 2 porciones.La función debe devolver un nuevo vector con las cantidades necesarias para el número deseado de porciones.
Sin embargo, quieres conservar la receta original.
Esto significa que el argumento quantities no debe modificarse 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 la imaginación, pero sus métodos son poco convencionales. No trabaja con listas ni medidas; habla en susurros, revelando solo un ingrediente a la vez.
Con una mezcla de emoción y aprensión, decides ponerte en contacto con tu tía bisabuela. Como esperabas, responde a su manera críptica, ofreciéndote un único ingrediente secreto, envuelto en misterio.
La contribución de tu tía bisabuela añade una capa de complejidad que quieres usar en tu receta.
Implementa otra versión de la función addSecretIngredient que acepta dos argumentos: una referencia a un vector que representa tu lista de ingredientes y un único string que representa la ofrenda 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.