Tracks
/
C++
C++
/
Übungen
/
Trolle die Trolle
Trolle die Trolle

Trolle die Trolle

Lernübung

Einführung

Aufzählungen

Bisher haben wir uns überwiegend mit Zahlen und Strings beschäftigt. Um die reale Welt abzubilden, möchten wir vielleicht eine begrenzte Anzahl von Werten haben, die eine Variable annehmen kann. Vielleicht möchtest du einen eigenen Typ mit ein paar unterschiedlichen Werten mit unterschiedlichen Namen. Zum Beispiel in einer Skateboard-Fabrik, wo das Material des Decks nur maple, bamboo oder plastic sein kann.

Du könntest Ganzzahlen verwenden, um diese Werte zu kodieren, aber du müsstest zusätzlichen Code schreiben, um zu prüfen, ob ein ungültiger Wert für das Material aus dem System kommt. Die Bedeutung dieser Magische Zahlen lässt sich im Quellcode schwer nachvollziehen, und sie führen leicht zu Verwechslungen. Mit enumerations förderst du ausdrucksstarken Code und schränkst unbeabsichtigte Vergleichsfehler ein.

Anwendungsbeispiel

Der genaue Begriff für diese Art von Aufzählung ist scoped enumeration. Das folgende Codefragment zeigt, wie du eine enumeration namens DeckMaterial schreibst. Beachte das Schlüsselwort enum class und das ; am Ende der Definition:

enum class DeckMaterial {
    maple,
    bamboo,
    plastic
};

Schau dir jetzt eine Preisfunktion im Skateboard-Laden an und beachte den Bereichsauflösungsoperator (::), der einen enumerator aus der enumeration angibt:

double deck_price(double base_price, DeckMaterial material) {
    if(material == DeckMaterial::plastic) {
        return base_price * 0.9;
    }
    return base_price * 1.3;
}

Gültigkeitsbereiche

Stell dir vor, du hast eine zweite enumeration für das Material der Rollen:

enum class WheelMaterial {
    steel,
    clay,
    plastic
};

Obwohl die Rollen und das Deck beide aus plastic bestehen können, lassen sich die beiden nicht verwechseln. Sie sind verschiedene Typen: DeckMaterial plastic und WheelMaterial plastic. Jede enumeration hat ihre enumerators in ihrem eigenen Gültigkeitsbereich, ihrem eigenen namespace. Deshalb heißen sie scoped enumerations.

Advanced

Aufzählungen ohne Gültigkeitsbereich

Vielleicht denkst du, dass es bei einem Namen wie scoped auch unscoped Aufzählungen geben müsste, und du hast recht. Unscoped enumerations werden immer unbeliebter, weil sie sich alle denselben globalen Namensraum teilen. Weil sie sich denselben Namensraum teilen, könntest du nicht zwei unscoped enumerations haben, deren enumerators gleich heißen, wie plastic im Beispiel oben.

Außerdem werden unscoped enumerations implizit in Ganzzahlen umgewandelt. Schau dir das folgende Beispiel an, das ein überraschendes Ergebnis liefert:

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!

Wenn du scoped enumerations in Ganzzahlen umwandeln willst, kannst du static_cast<int> verwenden.

switch-Anweisungen

Wie andere Sprachen bietet auch C++ eine switch-Anweisung. Eine switch-Anweisung ist eine kürzere Schreibweise für lange if ... else if-Anweisungen. Um eine switch-Anweisung zu erstellen, beginnen wir mit dem Schlüsselwort switch, gefolgt von einer Ganzzahl. Danach deklarieren wir jede einzelne Bedingung mit dem Schlüsselwort case. Wir können außerdem einen default-Fall deklarieren, der ausgeführt wird, wenn keine der vorherigen case-Bedingungen zutrifft. Jeder Fall sollte mit einer break- (oder return-)Anweisung enden.

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;
}

Fallthrough

Ein wichtiger Punkt am switch-Konstrukt ist, dass der Code so lange weiter ausgeführt wird, bis er durch eine break- (oder return-)Anweisung gestoppt wird. Das kann zu unerwartetem Verhalten führen.

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!

Der wichtigste Anwendungsfall für diese fortgesetzte Ausführung ist eine Anweisung mit mehreren Marken. Mehrere switch-Ergebnisse können auf denselben auszuführenden Codeabschnitt verweisen. So kann zum Beispiel in einer Buchungs-App die aufgerufene Funktion für die Gruppengrößen 2 und 3 dieselbe sein:

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

Anleitung

Deine Freundin Helma hat ein kleines Online-Spiel entwickelt, das schnell beliebt wurde. Es heißt HellMath. Die kleine Community zog einige Trolle an, die das Spiel und die Foren ziemlich unangenehm machen. Helma hat dich gebeten, an einem neuen Berechtigungssystem zu arbeiten, um Störenfriede von den anderen zu trennen.

Das Forum unterstützt drei verschiedene Aktionen:

  • Lesen
  • Schreiben
  • Entfernen

Es gibt vier Arten von Konten, jede mit anderen Standardberechtigungen:

  • Gäste: können Beiträge lesen.
  • Benutzer und Trolle: können Beiträge lesen und schreiben.
  • Moderatoren: können Beiträge lesen, schreiben und entfernen, sie haben alle Berechtigungen.

Helma hat bemerkt, dass es nichts bringt, Troll-Konten zu sperren. Ihre Strategie ist, ihnen die Illusion zu geben, ihre Zeit sei „gut investiert“, aber ihre Beiträge werden nur anderen Trollen angezeigt. Überall, wo eine Prioritätsreihenfolge nötig ist, stehen Trolle in jeder Reihenfolge ganz hinten. Wenn sie ein Spiel betreten, ist der Pool der verfügbaren Spieler ebenfalls auf andere Trolle beschränkt.

Diese Praxis nennt man Shadowbanning.

1. Richte Benutzertypen und Berechtigungen ein.

Definiere zuerst eine AccountStatus-Enumeration, die die vier Kontotypen darstellt: troll, guest, user und mod.

Definiere als Nächstes eine Action-Enumeration, die die drei Berechtigungstypen darstellt: read, write und remove.

2. Mache Trolle nur für andere Trolle sichtbar.

Jeder Beitrag in den Foren speichert den AccountStatus des Verfassers in seinen Metadaten. Stelle sicher, dass Beiträge von Trollen nur anderen Trollen angezeigt werden.

Helma braucht eine Funktion display_post, die zwei Argumente vom Typ AccountStatus erhält und ein bool zurückgibt. Das erste Argument ist der Status des Verfassers, das zweite ist der Status des Betrachters.

using namespace hellmath;
display_post(AccountStatus::troll, AccountStatus::user);
// => false
display_post(AccountStatus::mod, AccountStatus::guest);
// => true

3. Prüfe die Berechtigungen verschiedener Benutzer.

Helma braucht eine Möglichkeit zu prüfen, ob eine bestimmte Aktion für einen Benutzer erlaubt ist.

Implementiere bitte eine Funktion permission_check, die eine Action als erstes Argument und einen AccountStatus zum Prüfen entgegennimmt. Sie soll ein bool zurückgeben, entsprechend den Berechtigungen aus der Einleitung.

permission_check(Action::remove, AccountStatus::guest);
// => false
permission_check(Action::write, AccountStatus::mod);
// => true

4. Gewähre Spielzugang und bilde Spielerpaare.

Damit die eigentlichen Spieler im Spiel für ihre Handlungen zur Verantwortung gezogen werden können, verweigert Hellmath Gästen den Zugang. Wie oben erwähnt, möchte Helma, dass Trolle andere Trolle trollen. Spielverbindungen zwischen anderen Benutzern sind nicht eingeschränkt.

Implementiere die Funktion valid_player_combination, die prüft, ob zwei Spieler demselben Spiel beitreten können. Die Funktion hat zwei Parameter vom Typ AccountStatus und gibt ein bool zurück.

valid_player_combination(AccountStatus::guest, AccountStatus::mod);
// => false
valid_player_combination(AccountStatus::troll, AccountStatus::troll);
// => true

5. Erstelle eine Prioritätswarteschlange.

Durch das enorme Wachstum des Spiels und der Foren muss Helma jetzt Rechenleistung und Bandbreite unter den Benutzern verteilen. Um Notfälle zu bewältigen, bekommen Moderatoren die höchste Priorität. Gäste stehen in der Warteschlange hinter normalen Benutzern, und Trolle werden hinter allen anderen einsortiert.

Implementiere die Funktion has_priority, die zwei AccountStatus-Argumente entgegennimmt und genau dann true zurückgibt, wenn das erste Konto eine strikt höhere Priorität hat als das zweite.

has_priority(AccountStatus::guest, AccountStatus::mod);
// => false
has_priority(AccountStatus::user, AccountStatus::troll);
// => true
Über GitHub bearbeiten Der Link öffnet sich in einem neuen Fenster oder Tab
C++ Exercism

Bereit, mit Trolle die Trolle zu starten?

Melde dich bei Exercism an, um C++ mit 19 Konzepte100 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.