Appearance
C++ 异常处理
异常处理是 C++ 中处理运行时错误的重要机制。它允许程序在发生错误时优雅地处理错误,而不是直接崩溃。
1. 异常的基本概念
异常是程序在运行时发生的错误,如除以零、内存不足等。C++ 提供了异常处理机制来处理这些错误。
1.1 基本语法
cpp
try {
// 可能抛出异常的代码
} catch (异常类型 变量名) {
// 处理异常的代码
} catch (...) {
// 处理所有其他异常的代码
}示例
cpp
#include <iostream>
int main() {
try {
int a = 10;
int b = 0;
if (b == 0) {
throw "除数不能为零";
}
int result = a / b;
std::cout << "结果: " << result << std::endl;
} catch (const char* msg) {
std::cout << "错误: " << msg << std::endl;
}
return 0;
}2. 抛出异常
2.1 抛出基本类型异常
cpp
#include <iostream>
int main() {
try {
int age = -10;
if (age < 0) {
throw "年龄不能为负数";
}
std::cout << "年龄: " << age << std::endl;
} catch (const char* msg) {
std::cout << "错误: " << msg << std::endl;
}
return 0;
}2.2 抛出对象异常
cpp
#include <iostream>
#include <string>
class MyException {
private:
std::string message;
public:
MyException(std::string message) : message(message) {}
std::string what() {
return message;
}
};
int main() {
try {
int age = -10;
if (age < 0) {
throw MyException("年龄不能为负数");
}
std::cout << "年龄: " << age << std::endl;
} catch (MyException& e) {
std::cout << "错误: " << e.what() << std::endl;
}
return 0;
}2.3 抛出标准异常
cpp
#include <iostream>
#include <stdexcept>
int main() {
try {
int age = -10;
if (age < 0) {
throw std::invalid_argument("年龄不能为负数");
}
std::cout << "年龄: " << age << std::endl;
} catch (const std::invalid_argument& e) {
std::cout << "错误: " << e.what() << std::endl;
}
return 0;
}3. 捕获异常
3.1 捕获特定类型的异常
cpp
#include <iostream>
int main() {
try {
int a = 10;
int b = 0;
if (b == 0) {
throw "除数不能为零";
}
int result = a / b;
std::cout << "结果: " << result << std::endl;
} catch (const char* msg) {
std::cout << "错误: " << msg << std::endl;
}
return 0;
}3.2 捕获多个异常
cpp
#include <iostream>
int main() {
try {
int a = 10;
int b = 0;
if (b == 0) {
throw "除数不能为零";
}
int result = a / b;
std::cout << "结果: " << result << std::endl;
} catch (const char* msg) {
std::cout << "错误: " << msg << std::endl;
} catch (int e) {
std::cout << "错误代码: " << e << std::endl;
} catch (...) {
std::cout << "未知错误" << std::endl;
}
return 0;
}4. 标准异常
C++ 标准库提供了一些标准异常类,定义在<stdexcept>头文件中。
4.1 常用的标准异常
| 异常类 | 描述 |
|---|---|
std::exception | 所有标准异常的基类 |
std::runtime_error | 运行时错误 |
std::logic_error | 逻辑错误 |
std::invalid_argument | 无效参数 |
std::out_of_range | 超出范围 |
std::length_error | 长度错误 |
std::domain_error | 域错误 |
示例
cpp
#include <iostream>
#include <stdexcept>
#include <vector>
int main() {
try {
std::vector<int> vec = {1, 2, 3};
int value = vec.at(10); // 超出范围
} catch (const std::out_of_range& e) {
std::cout << "错误: " << e.what() << std::endl;
}
return 0;
}5. 自定义异常
5.1 继承 std::exception
cpp
#include <iostream>
#include <exception>
#include <string>
class MyException : public std::exception {
private:
std::string message;
public:
MyException(std::string message) : message(message) {}
const char* what() const noexcept override {
return message.c_str();
}
};
int main() {
try {
throw MyException("这是一个自定义异常");
} catch (const MyException& e) {
std::cout << "错误: " << e.what() << std::endl;
}
return 0;
}6. 异常规范
6.1 noexcept 关键字
noexcept关键字用于指定函数不会抛出异常。
cpp
#include <iostream>
void func1() noexcept {
std::cout << "这个函数不会抛出异常" << std::endl;
}
void func2() {
std::cout << "这个函数可能会抛出异常" << std::endl;
}
int main() {
func1();
func2();
return 0;
}6.2 动态异常规范(已弃用)
动态异常规范在 C++17 中已被弃用,不建议使用。
cpp
#include <iostream>
void func() throw(std::runtime_error) {
throw std::runtime_error("运行时错误");
}
int main() {
try {
func();
} catch (const std::runtime_error& e) {
std::cout << "错误: " << e.what() << std::endl;
}
return 0;
}7. 异常和资源管理
7.1 RAII(资源获取即初始化)
RAII 是一种资源管理技术,它确保资源在对象的生命周期内被正确管理。
cpp
#include <iostream>
#include <fstream>
class FileHandler {
private:
std::ofstream file;
public:
FileHandler(const std::string& filename) : file(filename) {
if (!file.is_open()) {
throw std::runtime_error("无法打开文件");
}
}
~FileHandler() {
if (file.is_open()) {
file.close();
}
}
void write(const std::string& content) {
file << content;
}
};
int main() {
try {
FileHandler handler("output.txt");
handler.write("Hello, World!");
} catch (const std::exception& e) {
std::cout << "错误: " << e.what() << std::endl;
}
return 0;
}7.2 使用 std::unique_ptr
cpp
#include <iostream>
#include <memory>
class Resource {
public:
Resource() {
std::cout << "资源被创建" << std::endl;
}
~Resource() {
std::cout << "资源被销毁" << std::endl;
}
void do_something() {
std::cout << "资源正在工作" << std::endl;
}
};
int main() {
try {
std::unique_ptr<Resource> resource = std::make_unique<Resource>();
resource->do_something();
} catch (const std::exception& e) {
std::cout << "错误: " << e.what() << std::endl;
}
return 0;
}8. 示例:综合运用
现在,让我们看一个综合运用各种异常处理特性的例子:
cpp
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>
class BankAccount {
private:
double balance;
public:
BankAccount(double initial_balance) : balance(initial_balance) {
if (initial_balance < 0) {
throw std::invalid_argument("初始余额不能为负数");
}
}
void deposit(double amount) {
if (amount <= 0) {
throw std::invalid_argument("存款金额必须大于零");
}
balance += amount;
}
void withdraw(double amount) {
if (amount <= 0) {
throw std::invalid_argument("取款金额必须大于零");
}
if (amount > balance) {
throw std::runtime_error("余额不足");
}
balance -= amount;
}
double get_balance() {
return balance;
}
};
class Student {
private:
std::string name;
int age;
std::vector<double> grades;
public:
Student(std::string name, int age) : name(name), age(age) {
if (age < 0) {
throw std::invalid_argument("年龄不能为负数");
}
if (age > 150) {
throw std::out_of_range("年龄超出范围");
}
}
void add_grade(double grade) {
if (grade < 0 || grade > 100) {
throw std::out_of_range("成绩必须在 0 到 100 之间");
}
grades.push_back(grade);
}
double get_average() {
if (grades.empty()) {
throw std::runtime_error("没有成绩");
}
double sum = 0;
for (double grade : grades) {
sum += grade;
}
return sum / grades.size();
}
void display() {
std::cout << "姓名: " << name << std::endl;
std::cout << "年龄: " << age << std::endl;
std::cout << "平均成绩: " << get_average() << std::endl;
}
};
int main() {
// 测试 BankAccount
std::cout << "测试 BankAccount:" << std::endl;
try {
BankAccount account(1000.0);
account.deposit(500.0);
account.withdraw(200.0);
std::cout << "余额: " << account.get_balance() << std::endl;
} catch (const std::invalid_argument& e) {
std::cout << "错误: " << e.what() << std::endl;
} catch (const std::runtime_error& e) {
std::cout << "错误: " << e.what() << std::endl;
}
std::cout << std::endl;
// 测试 Student
std::cout << "测试 Student:" << std::endl;
try {
Student student("张三", 20);
student.add_grade(90.0);
student.add_grade(85.0);
student.add_grade(95.0);
student.display();
} catch (const std::invalid_argument& e) {
std::cout << "错误: " << e.what() << std::endl;
} catch (const std::out_of_range& e) {
std::cout << "错误: " << e.what() << std::endl;
} catch (const std::runtime_error& e) {
std::cout << "错误: " << e.what() << std::endl;
}
return 0;
}小结
C++ 异常处理包括:
异常的基本概念:
- 异常是程序在运行时发生的错误
- 使用
try-catch块处理异常
抛出异常:
- 抛出基本类型异常
- 抛出对象异常
- 抛出标准异常
捕获异常:
- 捕获特定类型的异常
- 捕获多个异常
- 捕获所有异常
标准异常:
std::exception:所有标准异常的基类std::runtime_error:运行时错误std::logic_error:逻辑错误std::invalid_argument:无效参数std::out_of_range:超出范围
自定义异常:
- 继承
std::exception - 重写
what()方法
- 继承
异常规范:
noexcept关键字:指定函数不会抛出异常
异常和资源管理:
- RAII(资源获取即初始化)
- 使用智能指针管理资源
关键概念:
- 异常:程序在运行时发生的错误
- 抛出异常:使用
throw关键字抛出异常 - 捕获异常:使用
catch块捕获异常 - 标准异常:C++ 标准库提供的异常类
- 自定义异常:继承
std::exception创建自定义异常 - RAII:资源获取即初始化,一种资源管理技术
掌握异常处理是编写健壮 C++ 程序的重要基础,在后续章节中,我们将学习 C++ 的动态内存。