Menu
Coddy logo textTech

Klasy enum i silne typowanie

Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 80 z 104.

W C++11 wprowadzono klasy enum (zwane również enumeracjami zakresowymi), aby rozwiązać problemy tradycyjnych enumeracji w stylu C. Zapewniają większe bezpieczeństwo typów i zapobiegają przypadkowemu niewłaściwemu użyciu wartości enumeracji.

Tradycyjne typy wyliczeniowe mają problemy: ich wartości przedostają się do otaczającego zakresu i niejawnie konwertują się na liczby całkowite, co prowadzi do trudnych do wykrycia błędów:

// Tradycyjny enum – problematyczny
enum Color { Red, Green, Blue };
enum TrafficLight { Red, Yellow, Green };  // Błąd! Red i Green są już zdefiniowane

int x = Red;  // Niejawnie konwertuje na int (0)
if (Red == 0) { }  // Kompiluje się – porównanie enum z int

Klasy enum rozwiązują te problemy, ograniczając zakres wartości i zapobiegając niejawnym konwersjom:

enum class Color { Red, Green, Blue };
enum class TrafficLight { Red, Yellow, Green };  // OK - brak konfliktu

Color c = Color::Red;      // Trzeba użyć operatora zakresu
// int x = c;              // Błąd! Brak konwersji niejawnej
int x = static_cast<int>(c);  // Wymagana konwersja jawna

// if (c == 0) { }         // Błąd! Nie można porównać z int
if (c == Color::Red) { }   // OK - porównywane są wartości tego samego typu

Możesz także określić typ bazowy, aby kontrolować pamięć:

enum class Status : uint8_t {
    Inactive = 0,
    Active = 1,
    Pending = 2
};

Typy wyliczeniowe dobrze integrują się z projektami OOP, dzięki czemu kod jest czytelniejszy, a błędy typów są wykrywane podczas kompilacji, a nie w czasie działania programu.

challenge icon

Wyzwanie

Łatwy

Stwórzmy system priorytetów zadań, który wykorzystuje klasy enum do reprezentowania różnych poziomów priorytetów i statusów zadań. Pokazuje to, jak klasy enum zapewniają bezpieczeństwo typów i zapobiegają błędom występującym w przypadku tradycyjnych enumów.

Podzielisz kod na trzy pliki:

  • TaskTypes.h: Zdefiniuj klasy enum dla systemu zadań.

    Utwórz klasę enum Priority z wartościami: Low, Medium, High i Critical. Określ int jako typ bazowy, a wartości ustaw kolejno na 1, 2, 3 i 4.

    Utwórz klasę enum Status z wartościami: Pending, InProgress i Completed.

    Dodaj dwie funkcje pomocnicze:

    • priorityToString(Priority p): zwraca priorytet jako ciąg znaków ("Low", "Medium", "High" lub "Critical")
    • statusToString(Status s): zwraca status jako ciąg znaków ("Pending", "InProgress" lub "Completed")
  • Task.h: Zdefiniuj klasę Task, która korzysta z Twoich klas enum.

    Klasa Task powinna przechowywać nazwę (ciąg znaków), wartość Priority i wartość Status. Konstruktor powinien przyjmować nazwę i priorytet, a domyślnym statusem powinno być Status::Pending.

    Zaimplementuj następujące metody:

    • setStatus(Status s): aktualizuje status zadania
    • getPriorityValue(): zwraca bazową wartość całkowitą priorytetu przy użyciu static_cast
    • display(): wyświetla zadanie w formacie: [name] - Priority: [priority] (Value: [value]) - Status: [status]
  • main.cpp: Wczytaj trzy wartości:
    1. Nazwę zadania (ciąg znaków)
    2. Poziom priorytetu jako liczbę całkowitą (1=Low, 2=Medium, 3=High, 4=Critical)
    3. Status jako liczbę całkowitą (0=Pending, 1=InProgress, 2=Completed)

    Utwórz obiekt Task z podaną nazwą i priorytetem (przekonwertuj liczbę całkowitą na odpowiednią wartość enum Priority przy użyciu static_cast). Następnie ustaw jego status na podstawie trzeciej wartości wejściowej. Na koniec wywołaj display(), aby wyświetlić szczegóły zadania.

Na przykład, dla wartości wejściowych Fix bug, 3 i 1:

Fix bug - Priority: High (Value: 3) - Status: InProgress

Dla wartości wejściowych Write docs, 1 i 2:

Write docs - Priority: Low (Value: 1) - Status: Completed

Dla wartości wejściowych Deploy app, 4 i 0:

Deploy app - Priority: Critical (Value: 4) - Status: Pending

Zwróć uwagę, że klasy enum całkowicie oddzielają wartości priorytetu i statusu. Nie możesz przypadkowo porównać wartości Priority z Status ani przypisać jednej do drugiej. Jawne użycie static_cast, wymagane do konwersji między liczbami całkowitymi a wartościami enum, jasno określa intencję kodu i zapobiega subtelnym błędom.

Spróbuj swoich sił

#include <iostream>
#include <string>
#include "Task.h"

using namespace std;

int main() {
    // Wczytaj dane wejściowe
    string taskName;
    int priorityInt;
    int statusInt;
    
    getline(cin, taskName);
    cin >> priorityInt;
    cin >> statusInt;
    
    // TODO: Utwórz Task o podanej nazwie i priorytecie
    // Użyj static_cast, aby przekonwertować priorityInt na typ wyliczeniowy Priority
    
    // TODO: Ustaw status zadania na podstawie statusInt
    // Użyj static_cast, aby przekonwertować statusInt na typ wyliczeniowy Status
    
    // TODO: Wywołaj display(), aby wyświetlić szczegóły zadania
    
    return 0;
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C++ online