Introdução
Neste laboratório, você aprenderá os conceitos fundamentais de tratamento de exceções em C++. Começará por entender como lançar (throw) e capturar (catch) exceções básicas, que são uma forma de lidar com erros em tempo de execução ou situações inesperadas em seu programa. Em seguida, explorará o uso das palavras-chave try, catch e throw, bem como classes de exceção padrão como std::exception. Adicionalmente, aprenderá como definir classes de exceção personalizadas e implementar múltiplos blocos catch para diferentes tipos de exceção. Finalmente, explorará o uso de blocos try-catch aninhados para lidar com exceções em diferentes níveis do seu programa.
Lançar e Capturar Exceções Básicas
Nesta etapa, você aprenderá os conceitos fundamentais de tratamento de exceções em C++, focando em como lançar (throw) e capturar (catch) exceções básicas. Exceções são uma forma de lidar com erros em tempo de execução ou situações inesperadas em seu programa.
Abra o WebIDE e crie um novo arquivo chamado basic_exceptions.cpp no diretório ~/project:
touch ~/project/basic_exceptions.cpp
Adicione o seguinte código a basic_exceptions.cpp:
#include <iostream>
#include <stdexcept>
int divide(int numerator, int denominator) {
// Throw an exception if denominator is zero
if (denominator == 0) {
throw std::runtime_error("Division by zero is not allowed!");
}
return numerator / denominator;
}
int main() {
try {
// Attempt a normal division
int result1 = divide(10, 2);
std::cout << "10 / 2 = " << result1 << std::endl;
// Attempt division by zero
int result2 = divide(10, 0);
std::cout << "This line will not be executed" << std::endl;
}
catch (const std::exception& e) {
// Catch and handle the exception
std::cout << "Error: " << e.what() << std::endl;
}
return 0;
}
Vamos detalhar os componentes-chave:
Palavra-chave
throw:- Usada para gerar uma exceção quando um erro ocorre
- Neste exemplo, lançamos um
std::runtime_errorquando a divisão por zero é tentada
Bloco
try:- Contém código que pode gerar uma exceção
- Permite que você tente operações arriscadas
- Se uma exceção ocorrer, o controle do programa se move para o bloco
catch
Bloco
catch:- Lida com a exceção lançada no bloco
try - Captura exceções de um tipo específico (aqui,
std::exception) - Usa
e.what()para obter a mensagem de erro
- Lida com a exceção lançada no bloco
Compile e execute o programa:
g++ basic_exceptions.cpp -o basic_exceptions
./basic_exceptions
Exemplo de saída:
10 / 2 = 5
Error: Division by zero is not allowed!
Pontos-chave sobre o tratamento básico de exceções:
- Exceções fornecem uma maneira de lidar com erros em tempo de execução de forma elegante
throwgera uma exceçãotryecatchtrabalham juntos para gerenciar situações excepcionais- Evita falhas no programa, fornecendo tratamento de erros controlado
Entenda as Palavras-Chave try, catch e throw
Nesta etapa, você se aprofundará nas três palavras-chave principais do tratamento de exceções em C++: try, catch e throw. Essas palavras-chave trabalham juntas para criar um mecanismo robusto de tratamento de erros em seus programas.
Abra o WebIDE e crie um novo arquivo chamado exception_keywords.cpp no diretório ~/project:
touch ~/project/exception_keywords.cpp
Adicione o seguinte código a exception_keywords.cpp:
#include <iostream>
#include <string>
// Function that might throw an exception
int processAge(int age) {
// Throw an exception for invalid age
if (age < 0) {
throw std::string("Age cannot be negative");
}
if (age > 120) {
throw std::string("Age is unrealistically high");
}
return age;
}
int main() {
// First try block: handling age validation
try {
// Successful case
int validAge = processAge(25);
std::cout << "Valid age: " << validAge << std::endl;
// This will throw an exception
int invalidAge1 = processAge(-5);
std::cout << "This line will not be executed" << std::endl;
}
catch (const std::string& errorMessage) {
// Catch block to handle the thrown exception
std::cout << "Error: " << errorMessage << std::endl;
}
// Second try block: another example
try {
// This will throw another exception
int invalidAge2 = processAge(150);
std::cout << "This line will also not be executed" << std::endl;
}
catch (const std::string& errorMessage) {
std::cout << "Error: " << errorMessage << std::endl;
}
return 0;
}
Vamos detalhar os componentes-chave:
Palavra-chave
throw:- Usada para gerar uma exceção quando uma condição específica é atendida
- Pode lançar diferentes tipos de objetos (strings, inteiros, objetos personalizados)
- Interrompe imediatamente a execução da função atual
Bloco
try:- Contém código que pode gerar uma exceção
- Permite que você tente operações arriscadas
- Se uma exceção ocorrer, o controle do programa se move para o bloco
catchcorrespondente
Bloco
catch:- Captura e lida com tipos específicos de exceções
- Pode ter múltiplos blocos catch para diferentes tipos de exceção
- Impede que o programa trave, tratando os erros de forma elegante
Compile e execute o programa:
g++ exception_keywords.cpp -o exception_keywords
./exception_keywords
Exemplo de saída:
Valid age: 25
Error: Age cannot be negative
Error: Age is unrealistically high
Pontos-chave sobre as palavras-chave de exceção:
throwsinaliza uma condição de errotrydefine um bloco de código que pode gerar uma exceçãocatchlida com a exceção e impede a terminação do programa- Exceções fornecem uma maneira estruturada de gerenciar erros em tempo de execução
Usar Classes de Exceção Padrão (std::exception)
Nesta etapa, você aprenderá sobre as classes de exceção padrão em C++ e como usar a hierarquia std::exception para lidar com diferentes tipos de erros em tempo de execução. A Biblioteca Padrão C++ fornece um conjunto de classes de exceção predefinidas que cobrem vários cenários de erro.
Abra o WebIDE e crie um novo arquivo chamado standard_exceptions.cpp no diretório ~/project:
touch ~/project/standard_exceptions.cpp
Adicione o seguinte código a standard_exceptions.cpp:
#include <iostream>
#include <stdexcept>
#include <limits>
double divideNumbers(double numerator, double denominator) {
// Check for division by zero using std::runtime_error
if (denominator == 0) {
throw std::runtime_error("Division by zero is not allowed!");
}
return numerator / denominator;
}
void checkArrayIndex(int* arr, int size, int index) {
// Check for out-of-range access using std::out_of_range
if (index < 0 || index >= size) {
throw std::out_of_range("Array index is out of bounds!");
}
std::cout << "Value at index " << index << ": " << arr[index] << std::endl;
}
int main() {
try {
// Demonstrate division by zero exception
std::cout << "Attempting division:" << std::endl;
double result = divideNumbers(10, 0);
}
catch (const std::runtime_error& e) {
std::cout << "Runtime Error: " << e.what() << std::endl;
}
try {
// Demonstrate array index out of range exception
int numbers[] = {1, 2, 3, 4, 5};
int arraySize = 5;
std::cout << "\nAccessing array elements:" << std::endl;
checkArrayIndex(numbers, arraySize, 2); // Valid index
checkArrayIndex(numbers, arraySize, 10); // Invalid index
}
catch (const std::out_of_range& e) {
std::cout << "Out of Range Error: " << e.what() << std::endl;
}
return 0;
}
Vamos explorar as classes de exceção padrão:
std::exception:- Classe base para todas as exceções padrão
- Fornece um método virtual
what()para obter a descrição do erro
Classes de exceção derivadas comuns:
std::runtime_error: Para erros em tempo de execução que só podem ser detectados durante a execução do programastd::out_of_range: Quando um índice ou iterador está fora do intervalo válido- Outras classes comuns incluem
std::logic_error,std::invalid_argument, etc.
Compile e execute o programa:
g++ standard_exceptions.cpp -o standard_exceptions
./standard_exceptions
Exemplo de saída:
Attempting division:
Runtime Error: Division by zero is not allowed!
Accessing array elements:
Value at index 2: 3
Out of Range Error: Array index is out of bounds!
Pontos-chave sobre as classes de exceção padrão:
- Fornecem uma maneira estruturada de lidar com diferentes tipos de erros
- Cada classe de exceção serve a um propósito específico
- O método
what()retorna uma mensagem de erro descritiva - Ajuda a criar um tratamento de erros mais robusto e informativo
Definir Classes de Exceção Personalizadas
Nesta etapa, você aprenderá como criar suas próprias classes de exceção personalizadas em C++. Exceções personalizadas permitem que você defina tipos de erro específicos adaptados aos requisitos exclusivos de sua aplicação.
Abra o WebIDE e crie um novo arquivo chamado custom_exceptions.cpp no diretório ~/project:
touch ~/project/custom_exceptions.cpp
Adicione o seguinte código a custom_exceptions.cpp:
#include <iostream>
#include <string>
#include <stdexcept>
// Custom exception class for bank account errors
class InsufficientFundsException : public std::runtime_error {
public:
// Constructor that takes account balance and withdrawal amount
InsufficientFundsException(double balance, double amount)
: std::runtime_error("Insufficient funds"),
currentBalance(balance),
withdrawalAmount(amount) {}
// Method to get detailed error information
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) {
// Check if withdrawal amount exceeds current balance
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 {
// Create a bank account with initial balance
BankAccount account(100.0);
// Attempt a valid withdrawal
account.withdraw(50.0);
// Attempt an invalid withdrawal
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;
}
Vamos detalhar a classe de exceção personalizada:
Herança de
std::runtime_error:- Fornece uma base para classes de exceção personalizadas
- Permite usar o método
what()para obter a descrição do erro
Recursos de Exceção Personalizada:
- Construtor com contexto de erro adicional
- Métodos para recuperar detalhes específicos do erro
- Fornece um tratamento de erros mais informativo
Compile e execute o programa:
g++ custom_exceptions.cpp -o custom_exceptions
./custom_exceptions
Exemplo de saída:
Withdrawal successful. Remaining balance: $50
Error: Insufficient funds
Current Balance: $50
Attempted Withdrawal: $75
Pontos-chave sobre as classes de exceção personalizadas:
- Herda de classes de exceção padrão
- Adiciona contexto e métodos de erro específicos
- Fornece informações de erro mais detalhadas
- Melhora o tratamento de erros e a depuração
Implementar Múltiplos Blocos catch para Diferentes Tipos de Exceção
Nesta etapa, você aprenderá como lidar com múltiplos tipos de exceção usando diferentes blocos catch. Essa abordagem permite que você forneça tratamento de erro específico para vários tipos de exceções que podem ocorrer em seu programa.
Abra o WebIDE e crie um novo arquivo chamado multiple_catch.cpp no diretório ~/project:
touch ~/project/multiple_catch.cpp
Adicione o seguinte código a multiple_catch.cpp:
#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) {
// Validate name length
if (studentName.length() < 2) {
throw std::length_error("Name is too short");
}
// Validate age range
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;
// First attempt: Short name
try {
student.setStudent("A", 25);
}
catch (const std::length_error& e) {
std::cout << "Length Error: " << e.what() << std::endl;
}
catch (const InvalidAgeException& e) {
std::cout << "Age Error: " << e.what()
<< " (" << e.getInvalidAge() << ")" << std::endl;
}
// Second attempt: Invalid age
try {
student.setStudent("John Doe", 150);
}
catch (const std::length_error& e) {
std::cout << "Length Error: " << e.what() << std::endl;
}
catch (const InvalidAgeException& e) {
std::cout << "Age Error: " << e.what()
<< " (" << e.getInvalidAge() << ")" << std::endl;
}
// Successful attempt
try {
student.setStudent("Alice", 20);
student.displayInfo();
}
catch (const std::length_error& e) {
std::cout << "Length Error: " << e.what() << std::endl;
}
catch (const InvalidAgeException& e) {
std::cout << "Age Error: " << e.what()
<< " (" << e.getInvalidAge() << ")" << std::endl;
}
return 0;
}
Vamos detalhar os múltiplos blocos catch:
Múltiplos blocos
catch:- Permitem o tratamento de diferentes tipos de exceção
- São executados em ordem, do mais específico para o mais geral
- Cada bloco pode lidar com um tipo específico de exceção
Estratégia de Tratamento de Exceção:
- O primeiro bloco
catchlida comstd::length_error - O segundo bloco
catchlida comInvalidAgeException - Fornece mensagens de erro específicas para diferentes cenários
- O primeiro bloco
Compile e execute o programa:
g++ multiple_catch.cpp -o multiple_catch
./multiple_catch
Exemplo de saída:
Length Error: Name is too short
Age Error: Invalid age (150)
Name: Alice, Age: 20
Pontos-chave sobre múltiplos blocos catch:
- Lidam com diferentes tipos de exceção separadamente
- Fornecem tratamento de erro específico para cada exceção
- A ordem importa ao capturar exceções
- Permite um gerenciamento de erros mais granular
Usar Blocos try-catch Aninhados
Nesta etapa, você aprenderá como usar blocos try-catch aninhados para lidar com cenários de erro complexos e fornecer um tratamento de exceção mais granular. Blocos try-catch aninhados permitem que você lide com exceções em diferentes níveis do seu código.
Abra o WebIDE e crie um novo arquivo chamado nested_exceptions.cpp no diretório ~/project:
touch ~/project/nested_exceptions.cpp
Adicione o seguinte código a nested_exceptions.cpp:
#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 {
// Simulate file reading
if (filename.empty()) {
throw FileReadError("Empty filename");
}
std::cout << "Reading file: " << filename << std::endl;
try {
// Simulate data processing
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;
// Rethrow the exception to outer 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) {
// Simulate data validation
if (filename == "corrupt.txt") {
throw std::runtime_error("Corrupt file data");
}
std::cout << "Data validation successful" << std::endl;
}
};
int main() {
DataProcessor processor;
// Scenario 1: Empty filename
std::cout << "Scenario 1: Empty Filename" << std::endl;
processor.processFile("");
// Scenario 2: Corrupt file
std::cout << "\nScenario 2: Corrupt File" << std::endl;
processor.processFile("corrupt.txt");
// Scenario 3: Valid file
std::cout << "\nScenario 3: Valid File" << std::endl;
processor.processFile("data.txt");
return 0;
}
Vamos detalhar os blocos try-catch aninhados:
Bloco
try-catchexterno:- Lida com exceções em nível de arquivo
- Captura
FileReadErrore outros erros potenciais
Bloco
try-catchinterno:- Lida com exceções específicas do processamento de dados
- Pode relançar (rethrow) exceções para o bloco
catchexterno
Manipulador catch-all (
catch (...)):- Captura quaisquer exceções inesperadas
- Fornece uma camada final de tratamento de erros
Compile e execute o programa:
g++ nested_exceptions.cpp -o nested_exceptions
./nested_exceptions
Exemplo de saída:
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
Pontos-chave sobre blocos try-catch aninhados:
- Fornecem múltiplos níveis de tratamento de exceção
- Permitem um gerenciamento de erros mais detalhado
- Podem relançar exceções para blocos
catchexternos - Úteis para cenários de erro complexos
Resumo
Neste laboratório, você aprendeu os conceitos fundamentais de tratamento de exceções em C++. Você começou entendendo como lançar (throw) e capturar (catch) exceções básicas usando as palavras-chave throw, try e catch. Você explorou o uso da classe de exceção std::runtime_error para lidar com erros de divisão por zero. Além disso, você aprendeu como acessar a mensagem de erro usando a função e.what() dentro do bloco catch. Essas técnicas fornecem uma maneira de lidar com erros de tempo de execução de forma elegante e evitar travamentos do programa, permitindo que você escreva aplicações C++ mais robustas e confiáveis.



