Einführung
In diesem Lab werden Sie die grundlegenden Konzepte der Ausnahmebehandlung (Exception Handling) in C++ lernen. Sie beginnen damit, zu verstehen, wie Sie grundlegende Ausnahmen werfen (throw) und fangen (catch) können. Dies ist eine Möglichkeit, Laufzeitfehler oder unerwartete Situationen in Ihrem Programm zu behandeln. Anschließend werden Sie die Verwendung der Schlüsselwörter try, catch und throw sowie von Standard-Ausnahmeklassen wie std::exception untersuchen. Darüber hinaus werden Sie lernen, wie Sie benutzerdefinierte Ausnahmeklassen definieren und mehrere catch-Blöcke für verschiedene Ausnahmetypen implementieren können. Abschließend werden Sie die Verwendung von geschachtelten try-catch-Blöcken untersuchen, um Ausnahmen auf verschiedenen Ebenen Ihres Programms zu behandeln.
Grundlegende Ausnahmen werfen und fangen
In diesem Schritt werden Sie die grundlegenden Konzepte der Ausnahmebehandlung (Exception Handling) in C++ lernen, wobei der Schwerpunkt auf dem Werfen und Fangen von grundlegenden Ausnahmen liegt. Ausnahmen sind eine Möglichkeit, Laufzeitfehler oder unerwartete Situationen in Ihrem Programm zu behandeln.
Öffnen Sie die WebIDE und erstellen Sie eine neue Datei namens basic_exceptions.cpp im Verzeichnis ~/project:
touch ~/project/basic_exceptions.cpp
Fügen Sie den folgenden Code in die Datei basic_exceptions.cpp ein:
#include <iostream>
#include <stdexcept>
int divide(int numerator, int denominator) {
// Werfen Sie eine Ausnahme, wenn der Nenner Null ist
if (denominator == 0) {
throw std::runtime_error("Division durch Null ist nicht erlaubt!");
}
return numerator / denominator;
}
int main() {
try {
// Versuchen Sie eine normale Division
int result1 = divide(10, 2);
std::cout << "10 / 2 = " << result1 << std::endl;
// Versuchen Sie die Division durch Null
int result2 = divide(10, 0);
std::cout << "Diese Zeile wird nicht ausgeführt" << std::endl;
}
catch (const std::exception& e) {
// Fangen und behandeln Sie die Ausnahme
std::cout << "Fehler: " << e.what() << std::endl;
}
return 0;
}
Lassen Sie uns die wichtigsten Bestandteile analysieren:
Schlüsselwort
throw:- Wird verwendet, um eine Ausnahme zu generieren, wenn ein Fehler auftritt
- In diesem Beispiel werfen wir eine
std::runtime_error, wenn eine Division durch Null versucht wird
try-Block:- Enthält Code, der eine Ausnahme generieren kann
- Ermöglicht es Ihnen, riskante Operationen auszuführen
- Wenn eine Ausnahme auftritt, wird die Programmsteuerung an den
catch-Block übergeben
catch-Block:- Behandelt die im
try-Block geworfene Ausnahme - Fängt Ausnahmen eines bestimmten Typs (hier
std::exception) - Nutzt
e.what(), um die Fehlermeldung zu erhalten
- Behandelt die im
Kompilieren und führen Sie das Programm aus:
g++ basic_exceptions.cpp -o basic_exceptions
./basic_exceptions
Beispielausgabe:
10 / 2 = 5
Fehler: Division durch Null ist nicht erlaubt!
Wichtige Punkte zur grundlegenden Ausnahmebehandlung:
- Ausnahmen bieten eine Möglichkeit, Laufzeitfehler elegant zu behandeln
throwgeneriert eine Ausnahmetryundcatcharbeiten zusammen, um außergewöhnliche Situationen zu verwalten- Verhindert Programmabstürze durch kontrollierte Fehlerbehandlung
Verstehen Sie die Schlüsselwörter try, catch und throw
In diesem Schritt werden Sie tiefer in die drei wichtigsten Schlüsselwörter der Ausnahmebehandlung (Exception Handling) in C++ eintauchen: try, catch und throw. Diese Schlüsselwörter arbeiten zusammen, um einen robusten Fehlerbehandlungsmechanismus in Ihren Programmen zu schaffen.
Öffnen Sie die WebIDE und erstellen Sie eine neue Datei namens exception_keywords.cpp im Verzeichnis ~/project:
touch ~/project/exception_keywords.cpp
Fügen Sie den folgenden Code in die Datei exception_keywords.cpp ein:
#include <iostream>
#include <string>
// Funktion, die möglicherweise eine Ausnahme wirft
int processAge(int age) {
// Werfen Sie eine Ausnahme für ein ungültiges Alter
if (age < 0) {
throw std::string("Das Alter kann nicht negativ sein");
}
if (age > 120) {
throw std::string("Das Alter ist unrealistisch hoch");
}
return age;
}
int main() {
// Erster try-Block: Überprüfung des Alters
try {
// Erfolgsfall
int validAge = processAge(25);
std::cout << "Gültiges Alter: " << validAge << std::endl;
// Dies wird eine Ausnahme werfen
int invalidAge1 = processAge(-5);
std::cout << "Diese Zeile wird nicht ausgeführt" << std::endl;
}
catch (const std::string& errorMessage) {
// Catch-Block, um die geworfene Ausnahme zu behandeln
std::cout << "Fehler: " << errorMessage << std::endl;
}
// Zweiter try-Block: Ein weiteres Beispiel
try {
// Dies wird eine andere Ausnahme werfen
int invalidAge2 = processAge(150);
std::cout << "Diese Zeile wird ebenfalls nicht ausgeführt" << std::endl;
}
catch (const std::string& errorMessage) {
std::cout << "Fehler: " << errorMessage << std::endl;
}
return 0;
}
Lassen Sie uns die wichtigsten Bestandteile analysieren:
Schlüsselwort
throw:- Wird verwendet, um eine Ausnahme zu generieren, wenn eine bestimmte Bedingung erfüllt ist
- Kann verschiedene Objekttypen (Strings, Ganzzahlen, benutzerdefinierte Objekte) werfen
- Beendet sofort die Ausführung der aktuellen Funktion
try-Block:- Enthält Code, der möglicherweise eine Ausnahme generiert
- Ermöglicht es Ihnen, riskante Operationen auszuführen
- Wenn eine Ausnahme auftritt, wird die Programmsteuerung an den passenden
catch-Block übergeben
catch-Block:- Fängt und behandelt bestimmte Typen von Ausnahmen
- Kann mehrere Catch-Blöcke für verschiedene Ausnahmetypen haben
- Verhindert, dass das Programm abstürzt, indem es Fehler elegant behandelt
Kompilieren und führen Sie das Programm aus:
g++ exception_keywords.cpp -o exception_keywords
./exception_keywords
Beispielausgabe:
Gültiges Alter: 25
Fehler: Das Alter kann nicht negativ sein
Fehler: Das Alter ist unrealistisch hoch
Wichtige Punkte zu den Ausnahme-Schlüsselwörtern:
throwsignalisiert einen Fehlerzustandtrydefiniert einen Codeblock, der möglicherweise eine Ausnahme generiertcatchbehandelt die Ausnahme und verhindert das Beenden des Programms- Ausnahmen bieten eine strukturierte Möglichkeit, Laufzeitfehler zu verwalten
Verwenden Sie Standard-Ausnahmeklassen (std::exception)
In diesem Schritt werden Sie über die Standard-Ausnahmeklassen in C++ lernen und wie Sie die std::exception-Hierarchie nutzen können, um verschiedene Arten von Laufzeitfehlern zu behandeln. Die C++-Standardbibliothek bietet eine Reihe von vordefinierten Ausnahmeklassen, die verschiedene Fehlerszenarien abdecken.
Öffnen Sie die WebIDE und erstellen Sie eine neue Datei namens standard_exceptions.cpp im Verzeichnis ~/project:
touch ~/project/standard_exceptions.cpp
Fügen Sie den folgenden Code in die Datei standard_exceptions.cpp ein:
#include <iostream>
#include <stdexcept>
#include <limits>
double divideNumbers(double numerator, double denominator) {
// Prüfen Sie auf Division durch Null mit std::runtime_error
if (denominator == 0) {
throw std::runtime_error("Division durch Null ist nicht erlaubt!");
}
return numerator / denominator;
}
void checkArrayIndex(int* arr, int size, int index) {
// Prüfen Sie auf Zugriff außerhalb des gültigen Bereichs mit std::out_of_range
if (index < 0 || index >= size) {
throw std::out_of_range("Array-Index ist außerhalb der Grenzen!");
}
std::cout << "Wert am Index " << index << ": " << arr[index] << std::endl;
}
int main() {
try {
// Demonstrieren Sie die Division-durch-Null-Ausnahme
std::cout << "Versuch einer Division:" << std::endl;
double result = divideNumbers(10, 0);
}
catch (const std::runtime_error& e) {
std::cout << "Laufzeitfehler: " << e.what() << std::endl;
}
try {
// Demonstrieren Sie die Array-Index-ausserhalb-des-Bereichs-Ausnahme
int numbers[] = {1, 2, 3, 4, 5};
int arraySize = 5;
std::cout << "\nZugriff auf Array-Elemente:" << std::endl;
checkArrayIndex(numbers, arraySize, 2); // Gültiger Index
checkArrayIndex(numbers, arraySize, 10); // Ungültiger Index
}
catch (const std::out_of_range& e) {
std::cout << "Index-ausserhalb-des-Bereichs-Fehler: " << e.what() << std::endl;
}
return 0;
}
Lassen Sie uns die Standard-Ausnahmeklassen untersuchen:
std::exception:- Basisklasse für alle Standard-Ausnahmen
- Bietet eine virtuelle
what()-Methode, um die Fehlbeschreibung zu erhalten
Häufige abgeleitete Ausnahmeklassen:
std::runtime_error: Für Laufzeitfehler, die erst während der Programmausführung erkannt werden könnenstd::out_of_range: Wenn ein Index oder Iterator außerhalb des gültigen Bereichs liegt- Andere häufige Klassen sind
std::logic_error,std::invalid_argumentusw.
Kompilieren und führen Sie das Programm aus:
g++ standard_exceptions.cpp -o standard_exceptions
./standard_exceptions
Beispielausgabe:
Versuch einer Division:
Laufzeitfehler: Division durch Null ist nicht erlaubt!
Zugriff auf Array-Elemente:
Wert am Index 2: 3
Index-ausserhalb-des-Bereichs-Fehler: Array-Index ist außerhalb der Grenzen!
Wichtige Punkte zu den Standard-Ausnahmeklassen:
- Bieten eine strukturierte Möglichkeit, verschiedene Arten von Fehlern zu behandeln
- Jede Ausnahmeklasse hat einen bestimmten Zweck
- Die
what()-Methode gibt eine beschreibende Fehlermeldung zurück - Hilft bei der Erstellung einer robusteren und informativeren Fehlerbehandlung
Benutzerdefinierte Ausnahmeklassen definieren
In diesem Schritt werden Sie lernen, wie Sie Ihre eigenen benutzerdefinierten Ausnahmeklassen in C++ erstellen können. Benutzerdefinierte Ausnahmen ermöglichen es Ihnen, spezifische Fehlertypen zu definieren, die auf die einzigartigen Anforderungen Ihrer Anwendung zugeschnitten sind.
Öffnen Sie die WebIDE und erstellen Sie eine neue Datei namens custom_exceptions.cpp im Verzeichnis ~/project:
touch ~/project/custom_exceptions.cpp
Fügen Sie den folgenden Code in die Datei custom_exceptions.cpp ein:
#include <iostream>
#include <string>
#include <stdexcept>
// Benutzerdefinierte Ausnahmeklasse für Bankkontofehler
class InsufficientFundsException : public std::runtime_error {
public:
// Konstruktor, der den Kontostand und den Abhebungsbetrag entgegennimmt
InsufficientFundsException(double balance, double amount)
: std::runtime_error("Insufficient funds"),
currentBalance(balance),
withdrawalAmount(amount) {}
// Methode, um detaillierte Fehlerinformationen zu erhalten
double getCurrentBalance() const { return currentBalance; }
double getWithdrawalAmount() const { return withdrawalAmount; }
private:
double currentBalance;
double withdrawalAmount;
};
class BankAccount {
private:
double balance;
public:
BankAccount(double initialBalance) : balance(initialBalance) {}
void withdraw(double amount) {
// Prüfen, ob der Abhebungsbetrag den aktuellen Kontostand überschreitet
if (amount > balance) {
throw InsufficientFundsException(balance, amount);
}
balance -= amount;
std::cout << "Withdrawal successful. Remaining balance: $"
<< balance << std::endl;
}
double getBalance() const { return balance; }
};
int main() {
try {
// Erstellen Sie ein Bankkonto mit einem Anfangssaldo
BankAccount account(100.0);
// Versuchen Sie eine gültige Abhebung
account.withdraw(50.0);
// Versuchen Sie eine ungültige Abhebung
account.withdraw(75.0);
}
catch (const InsufficientFundsException& e) {
std::cout << "Error: " << e.what() << std::endl;
std::cout << "Current Balance: $" << e.getCurrentBalance() << std::endl;
std::cout << "Attempted Withdrawal: $" << e.getWithdrawalAmount() << std::endl;
}
return 0;
}
Lassen Sie uns die benutzerdefinierte Ausnahmeklasse analysieren:
Ableitung von
std::runtime_error:- Bietet eine Basis für benutzerdefinierte Ausnahmeklassen
- Ermöglicht die Verwendung der
what()-Methode, um die Fehlbeschreibung zu erhalten
Merkmale der benutzerdefinierten Ausnahme:
- Konstruktor mit zusätzlichem Fehlerkontext
- Methoden zur Abfrage spezifischer Fehlerdetails
- Bietet eine informativere Fehlerbehandlung
Kompilieren und führen Sie das Programm aus:
g++ custom_exceptions.cpp -o custom_exceptions
./custom_exceptions
Beispielausgabe:
Withdrawal successful. Remaining balance: $50
Error: Insufficient funds
Current Balance: $50
Attempted Withdrawal: $75
Wichtige Punkte zu benutzerdefinierten Ausnahmeklassen:
- Ableitung von Standard-Ausnahmeklassen
- Hinzufügen von spezifischem Fehlerkontext und Methoden
- Bereitstellung detaillierterer Fehlerinformationen
- Verbesserung der Fehlerbehandlung und des Debuggings
Mehrere catch-Blöcke für verschiedene Ausnahmetypen implementieren
In diesem Schritt werden Sie lernen, wie Sie verschiedene Ausnahmetypen mithilfe von mehreren Catch-Blöcken behandeln können. Dieser Ansatz ermöglicht es Ihnen, spezifische Fehlerbehandlungen für verschiedene Arten von Ausnahmen bereitzustellen, die in Ihrem Programm auftreten können.
Öffnen Sie die WebIDE und erstellen Sie eine neue Datei namens multiple_catch.cpp im Verzeichnis ~/project:
touch ~/project/multiple_catch.cpp
Fügen Sie den folgenden Code in die Datei multiple_catch.cpp ein:
#include <iostream>
#include <stdexcept>
#include <string>
class InvalidAgeException : public std::runtime_error {
public:
InvalidAgeException(int age)
: std::runtime_error("Invalid age"), invalidAge(age) {}
int getInvalidAge() const { return invalidAge; }
private:
int invalidAge;
};
class Student {
private:
std::string name;
int age;
public:
void setStudent(const std::string& studentName, int studentAge) {
// Validieren Sie die Länge des Namens
if (studentName.length() < 2) {
throw std::length_error("Name is too short");
}
// Validieren Sie den Altersbereich
if (studentAge < 0 || studentAge > 120) {
throw InvalidAgeException(studentAge);
}
name = studentName;
age = studentAge;
}
void displayInfo() const {
std::cout << "Name: " << name << ", Age: " << age << std::endl;
}
};
int main() {
Student student;
// Erster Versuch: Kurzer Name
try {
student.setStudent("A", 25);
}
catch (const std::length_error& e) {
std::cout << "Längenfehler: " << e.what() << std::endl;
}
catch (const InvalidAgeException& e) {
std::cout << "Altersfehler: " << e.what()
<< " (" << e.getInvalidAge() << ")" << std::endl;
}
// Zweiter Versuch: Ungültiges Alter
try {
student.setStudent("John Doe", 150);
}
catch (const std::length_error& e) {
std::cout << "Längenfehler: " << e.what() << std::endl;
}
catch (const InvalidAgeException& e) {
std::cout << "Altersfehler: " << e.what()
<< " (" << e.getInvalidAge() << ")" << std::endl;
}
// Erfolgreicher Versuch
try {
student.setStudent("Alice", 20);
student.displayInfo();
}
catch (const std::length_error& e) {
std::cout << "Längenfehler: " << e.what() << std::endl;
}
catch (const InvalidAgeException& e) {
std::cout << "Altersfehler: " << e.what()
<< " (" << e.getInvalidAge() << ")" << std::endl;
}
return 0;
}
Lassen Sie uns die mehreren Catch-Blöcke analysieren:
Mehrere
catch-Blöcke:- Ermöglichen die Behandlung verschiedener Ausnahmetypen
- Werden in der Reihenfolge von spezifisch bis allgemein ausgeführt
- Jeder Block kann einen bestimmten Ausnahmetyp behandeln
Strategie zur Ausnahmebehandlung:
- Der erste Catch-Block behandelt
std::length_error - Der zweite Catch-Block behandelt
InvalidAgeException - Bietet spezifische Fehlermeldungen für verschiedene Szenarien
- Der erste Catch-Block behandelt
Kompilieren und führen Sie das Programm aus:
g++ multiple_catch.cpp -o multiple_catch
./multiple_catch
Beispielausgabe:
Längenfehler: Name is too short
Altersfehler: Invalid age (150)
Name: Alice, Age: 20
Wichtige Punkte zu mehreren Catch-Blöcken:
- Behandeln verschiedene Ausnahmetypen separat
- Bieten spezifische Fehlerbehandlungen für jede Ausnahme
- Die Reihenfolge ist beim Fangen von Ausnahmen wichtig
- Ermöglichen eine feinere Fehlerverwaltung
Geschachtelte try-catch-Blöcke verwenden
In diesem Schritt werden Sie lernen, wie Sie geschachtelte try-catch-Blöcke verwenden können, um komplexe Fehlerszenarien zu behandeln und eine feinere Ausnahmebehandlung zu ermöglichen. Geschachtelte try-catch-Blöcke ermöglichen es Ihnen, Ausnahmen auf verschiedenen Ebenen Ihres Codes zu behandeln.
Öffnen Sie die WebIDE und erstellen Sie eine neue Datei namens nested_exceptions.cpp im Verzeichnis ~/project:
touch ~/project/nested_exceptions.cpp
Fügen Sie den folgenden Code in die Datei nested_exceptions.cpp ein:
#include <iostream>
#include <stdexcept>
#include <string>
class FileReadError : public std::runtime_error {
public:
FileReadError(const std::string& filename)
: std::runtime_error("Error reading file"), fileName(filename) {}
std::string getFileName() const { return fileName; }
private:
std::string fileName;
};
class DataProcessor {
public:
void processFile(const std::string& filename) {
try {
// Simulieren Sie das Lesen einer Datei
if (filename.empty()) {
throw FileReadError("Empty filename");
}
std::cout << "Reading file: " << filename << std::endl;
try {
// Simulieren Sie die Datenverarbeitung
validateData(filename);
}
catch (const std::runtime_error& e) {
std::cout << "Inner try-catch: Data validation error" << std::endl;
std::cout << "Error details: " << e.what() << std::endl;
// Werfen Sie die Ausnahme erneut an den äußeren Catch-Block
throw;
}
}
catch (const FileReadError& e) {
std::cout << "Outer try-catch: File read error" << std::endl;
std::cout << "Filename: " << e.getFileName() << std::endl;
}
catch (...) {
std::cout << "Caught unknown exception" << std::endl;
}
}
private:
void validateData(const std::string& filename) {
// Simulieren Sie die Datenvalidierung
if (filename == "corrupt.txt") {
throw std::runtime_error("Corrupt file data");
}
std::cout << "Data validation successful" << std::endl;
}
};
int main() {
DataProcessor processor;
// Szenario 1: Leerer Dateiname
std::cout << "Scenario 1: Empty Filename" << std::endl;
processor.processFile("");
// Szenario 2: Beschädigte Datei
std::cout << "\nScenario 2: Corrupt File" << std::endl;
processor.processFile("corrupt.txt");
// Szenario 3: Gültige Datei
std::cout << "\nScenario 3: Valid File" << std::endl;
processor.processFile("data.txt");
return 0;
}
Lassen Sie uns die geschachtelten try-catch-Blöcke analysieren:
Äußerer try-catch-Block:
- Behandelt Datei-Ebene-Ausnahmen
- Fängt
FileReadErrorund andere potenzielle Fehler
Innerer try-catch-Block:
- Behandelt datenverarbeitungsspezifische Ausnahmen
- Kann Ausnahmen an den äußeren Catch-Block erneut werfen
Catch-all-Handler (
catch (...)):- Fängt alle unerwarteten Ausnahmen
- Bietet eine letzte Ebene der Fehlerbehandlung
Kompilieren und führen Sie das Programm aus:
g++ nested_exceptions.cpp -o nested_exceptions
./nested_exceptions
Beispielausgabe:
Scenario 1: Empty Filename
Outer try-catch: File read error
Filename: Empty filename
Scenario 2: Corrupt File
Reading file: corrupt.txt
Inner try-catch: Data validation error
Error details: Corrupt file data
Scenario 3: Valid File
Reading file: data.txt
Data validation successful
Wichtige Punkte zu geschachtelten try-catch-Blöcken:
- Bieten mehrere Ebenen der Ausnahmebehandlung
- Ermöglichen eine detailliertere Fehlerverwaltung
- Können Ausnahmen an äußere Catch-Blöcke erneut werfen
- Nützlich für komplexe Fehlerszenarien
Zusammenfassung
In diesem Lab haben Sie die grundlegenden Konzepte der Ausnahmebehandlung in C++ gelernt. Sie haben begonnen, zu verstehen, wie Sie grundlegende Ausnahmen mit den Schlüsselwörtern throw, try und catch werfen und fangen können. Sie haben die Verwendung der std::runtime_error-Ausnahmeklasse zur Behandlung von Divisionen durch Null erforscht. Darüber hinaus haben Sie gelernt, wie Sie die Fehlermeldung mithilfe der e.what()-Funktion innerhalb des catch-Blocks abrufen können. Diese Techniken bieten eine Möglichkeit, Laufzeitfehler elegant zu behandeln und Programmabstürze zu vermeiden, sodass Sie robusteres und zuverlässigeres C++-Software schreiben können.



