Du hast in einigen Konzepten bereits C++-Funktionen verwendet und geschrieben. Jetzt wird es technisch. Der folgende Codeausschnitt zeigt die gebräuchlichsten Begriffe zum Nachschlagen. Da C++ Leerraum ignoriert, wurde die Formatierung geändert, um jedes Element in eine einzelne Zeile zu setzen.
// 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
Die Deklaration funktioniert wie eine Notiz an den Compiler, dass es eine Funktion mit diesem Namen, diesem Rückgabetyp und dieser Parameterliste gibt. Der Code funktioniert nicht, wenn die Definition fehlt. Deklarationen sind optional. Sie werden gebraucht, wenn du die Funktion vor ihrer Definition verwendest. Deklarationen können Probleme wie zyklische Referenzen lösen, und sie können verwendet werden, um die Schnittstelle von der Implementierung zu trennen.
Manchmal möchtest du sicherstellen, dass Werte nach ihrer Initialisierung nicht mehr geändert werden können.
C++ verwendet das Schlüsselwort const als Qualifizierer für Konstanten.
const int number_of_dragon_balls{7};
number_of_dragon_balls--; // compilation error
Du wirst Konstanten oft in UPPER_SNAKE_CASE geschrieben sehen. Es wird empfohlen, diese Schreibweise für Makros zu reservieren, wenn es keine andere Konvention gibt.
Wenn du versuchst, eine konstante Variable nach ihrer Zuweisung zu ändern, wird dein Code nicht kompiliert. Das hilft, unbeabsichtigte Änderungen zu vermeiden, eröffnet aber auch Optimierungsmöglichkeiten für den Compiler. Auch als Mensch ist es einfacher, über den Code nachzudenken, wenn du weißt, dass bestimmte Teile nicht betroffen sind.
Du kannst const auch als Qualifizierer für Funktionsparameter verwenden.
string guess_number(const int& secret, const int& guess) {
if (secret < guess) return "lower.";
if (secret > guess) return "higher.";
return "exact!";
}
Wenn du eine const-Referenz an die Funktion übergibst, kannst du sicher sein, dass sie unverändert bleibt.
Du wirst oft const-Referenzen für Objekte sehen, deren Kopieren teuer sein kann, etwa bei längeren Strings.
Ein dritter Anwendungsfall für den const-Qualifizierer sind Memberfunktionen, die die Instanz einer Klasse nicht verändern.
class Stubborn {
public:
Stubborn(string reply) {
response = reply;
}
string answer(const string& question) const {
if (question.length() == 0) { return ""; }
return response;
}
private:
string response{};
};
Die Memberfunktion answer von Stubborn verwendet eine const string&-Referenz als Parameter.
Dadurch wird eine Kopie des ursprünglichen Objekts vermieden, das an die Funktion übergeben wurde.
Mehrere Funktionen können denselben Namen haben, wenn ihre Parameterliste unterschiedlich ist. Das nennt man Funktionsüberladung, und sie wird meist dann verwendet, wenn diese Funktionen sehr ähnliche Aufgaben erfüllen.
Der Funktionskopf ohne den Rückgabetyp ist die Typsignatur der Funktion. Eine Änderung der Typsignatur ergibt eine neue Funktion.
Das Beispiel play_sound hat sechs verschiedene Überladungen, um unterschiedliche Szenarien abzudecken:
// 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);
Die Typsignatur wird durch den Namen der Funktion, die Anzahl der Parameter, ihre Typen und ihre Qualifizierer definiert (aber nicht durch ihre Namen). Der Rückgabetyp ist ausdrücklich nicht Teil der Typsignatur. Du bekommst Kompilierungsfehler, wenn du zwei Funktionen hast, die sich nur im Rückgabetyp unterscheiden. Der Compiler beschwert sich, weil nicht klar ist, welche der beiden verwendet werden soll.
Manche Funktionen können sehr lang werden, und bei vielen ihrer Aufrufe werden für die meisten Parameter dieselben Werte verwendet. Die Wiederholung in diesen Aufrufen lässt sich mit Standardargumenten vermeiden.
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"
Da die Funktionsdeklaration oft vor der Definition gelesen wird, ist sie der bessere Ort, um die Standardargumente festzulegen. Wenn für einen Parameter ein Standardwert deklariert ist, müssen auch alle Parameter rechts davon einen Standardwert deklarieren. Manchmal lassen sich komplizierte Funktionsüberladungen zu weniger Funktionen mit Standardargumenten umgestalten, um die Wartbarkeit zu verbessern.
In dieser Übung schreibst du weiteren Code rund um die Vorbereitung und das Kochen deiner großartigen Lasagne aus deinem Lieblingskochbuch.
Es gibt fünf Aufgaben. Die erste dreht sich um das Kochen selbst, die anderen vier um die perfekte Vorbereitung.
Für die nächste Lasagne, die du zubereiten willst, möchtest du genug Zeit einplanen, damit du das Kochen genießen kannst. Du hast bereits geplant, welche Schichten deine Lasagne haben soll. Jetzt möchtest du daraus abschätzen, wie lange die Vorbereitung dauern wird.
Implementiere eine Funktion preparationTime, die einen Vektor von Schichten als string und die durchschnittliche Vorbereitungszeit pro Schicht in Minuten als int entgegennimmt.
Die Funktion soll die geschätzte Gesamtvorbereitungszeit anhand der Anzahl der Schichten als int zurückgeben.
Wenn die durchschnittliche Vorbereitungszeit nicht übergeben wird, soll der Standardwert 2 verwendet werden.
std::vector<std::string> layers{"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"};
preparationTime(layers, 3)
// => 18
preparationTime(layers)
// => 12
Neben der Zeitplanung möchtest du auch sicherstellen, dass du genug Sauce und Nudeln hast, um die Lasagne deiner Träume zu kochen. Für jede Nudelschicht in deiner Lasagne brauchst du 50 Gramm Nudeln. Für jede Saucenschicht in deiner Lasagne brauchst du 0,2 Liter Sauce.
Definiere die Funktion quantities, die einen Vektor von Schichten als Parameter vom Typ string entgegennimmt.
Die Funktion ermittelt dann die Menge an Nudeln und Sauce, die du für dein Gericht brauchst.
Das Ergebnis soll als amount struct zurückgegeben werden, das bereits für dich definiert ist.
amount hat zwei Mitgliedsvariablen: eine für das Gewicht der noodles als int und die zweite für das Volumen der sauce als double.
struct amount { int noodles; double sauce; };
amount needed{quantities({"sauce", "noodles", "sauce", "meat", "mozzarella", "noodles"})};
// needed.noodles => 100
// needed.sauce => 0.4
Vor einer Weile warst du bei einem Freund zu Besuch und hast dort Lasagne gegessen. Sie war fantastisch und hatte etwas ganz Besonderes an sich. Der Freund hat dir die Zutatenliste geschickt und dir gesagt, dass der letzte Eintrag auf der Liste die „geheime Zutat“ ist, die das Gericht so besonders gemacht hat. Jetzt möchtest du diese geheime Zutat auch zu deinem Rezept hinzufügen.
Schreibe eine Funktion addSecretIngredient, die zwei vector of strings als Parameter entgegennimmt.
Der erste Parameter ist eine Referenz auf die Zutaten deines eigenen Rezepts, und der zweite Vektor aus Strings enthält die Zutaten, die dein Freund dir geschickt hat.
Das letzte Element in deiner Zutatenliste ist immer "?".
Die Funktion soll es durch den letzten Eintrag aus der Liste deines Freundes ersetzen.
Hinweis: addSecretIngredient gibt nichts zurück. Du solltest die Liste deiner Zutaten direkt verändern.
Die Liste mit den Zutaten deines Freundes kann ebenfalls eine Referenz sein (um die Performance zu verbessern), aber sie sollte nicht verändert werden.
Stelle sicher, dass die Liste durch eine const-Referenz nicht verändert wird.
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"}
Die Mengen in deinem Kochbuch ergeben nur genug Lasagne für zwei Portionen. Da du nächstes Mal für mehr Personen kochen möchtest, willst du die Mengen für verschiedene Portionszahlen berechnen.
Implementiere eine Funktion scaleRecipe, die zwei Parameter entgegennimmt.
double-Mengen, die für 2 Portionen benötigt werden.Die Funktion soll einen neuen Vektor mit den Mengen zurückgeben, die für die gewünschte Anzahl an Portionen benötigt werden.
Das ursprüngliche Rezept solltest du allerdings beibehalten.
Das bedeutet, dass das Argument quantities in dieser Funktion nicht verändert werden darf.
std::vector<double> quantities{1.2, 3.6, 10.5};
scaleRecipe(quantities, 4);
// => { 2.4, 7.2, 21 }
Du erinnerst dich an Geschichten aus deiner Kindheit, Erzählungen von deiner Großtante, der Hüterin der tiefsten kulinarischen Geheimnisse der Familie. Der Legende nach hält sie den Schlüssel zu Aromen jenseits aller Vorstellungskraft, doch ihre Methoden sind unkonventionell. Sie arbeitet nicht mit Listen oder Maßeinheiten; sie spricht in Flüstertönen und verrät immer nur eine Zutat auf einmal.
Mit einer Mischung aus Aufregung und Beklommenheit beschließt du, dich an deine Großtante zu wenden. Wie erwartet antwortet sie auf ihre geheimnisvolle Art und nennt dir eine einzige geheime Zutat, gehüllt in Rätsel.
Der Beitrag deiner Großtante bringt eine zusätzliche Ebene der Komplexität ins Spiel, die du in deinem Rezept nutzen möchtest.
Implementiere eine weitere Version der Funktion addSecretIngredient, die zwei Argumente entgegennimmt: eine Referenz auf einen Vektor, der deine Zutatenliste darstellt, und einen einzelnen String, der das rätselhafte Angebot deiner Großtante darstellt.
std::vector<std::string> myList{"noodles", "meat", "sauce", "mozzarella", "?"};
std::string secretIngredient{"spice melange"};
addSecretIngredient(myList, secretIngredient);
// myList => {"noodles", "meat", "sauce", "mozzarella", "spice melange"}
Viel Glück, du mutiger Koch!
Melde dich bei Exercism an, um C++ mit 19 Konzepte100 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.