C++ 异常处理
概述
异常处理是C++的重要特性,用于处理程序运行时的错误情况。通过try-catch机制,程序可以优雅地处理异常,而不是崩溃终止。异常处理提高了程序的健壮性和可维护性。
⚠️ 异常处理基础
try-catch-throw机制
cpp
#include <iostream>
#include <stdexcept>
#include <string>
class Calculator {
public:
static double divide(double a, double b) {
if (b == 0) {
throw std::invalid_argument("除数不能为零");
}
return a / b;
}
static double squareRoot(double x) {
if (x < 0) {
throw std::domain_error("不能计算负数的平方根");
}
return std::sqrt(x);
}
static int factorial(int n) {
if (n < 0) {
throw std::invalid_argument("阶乘的参数不能为负数");
}
if (n > 20) {
throw std::overflow_error("阶乘结果太大");
}
int result = 1;
for (int i = 1; i <= n; ++i) {
result *= i;
}
return result;
}
};
int main() {
std::cout << "=== 异常处理基础 ===" << std::endl;
// 正常情况
try {
double result = Calculator::divide(10, 2);
std::cout << "10 / 2 = " << result << std::endl;
result = Calculator::squareRoot(16);
std::cout << "sqrt(16) = " << result << std::endl;
int fact = Calculator::factorial(5);
std::cout << "5! = " << fact << std::endl;
} catch (const std::exception& e) {
std::cout << "异常: " << e.what() << std::endl;
}
// 异常情况
std::cout << "\n--- 测试异常情况 ---" << std::endl;
try {
Calculator::divide(10, 0); // 除零异常
} catch (const std::invalid_argument& e) {
std::cout << "捕获invalid_argument: " << e.what() << std::endl;
}
try {
Calculator::squareRoot(-4); // 域错误
} catch (const std::domain_error& e) {
std::cout << "捕获domain_error: " << e.what() << std::endl;
}
try {
Calculator::factorial(25); // 溢出错误
} catch (const std::overflow_error& e) {
std::cout << "捕获overflow_error: " << e.what() << std::endl;
}
return 0;
}多重catch和异常层次
cpp
#include <iostream>
#include <stdexcept>
#include <memory>
class BankAccount {
private:
double balance_;
std::string accountNumber_;
public:
BankAccount(const std::string& number, double balance)
: accountNumber_(number), balance_(balance) {}
void withdraw(double amount) {
if (amount <= 0) {
throw std::invalid_argument("取款金额必须大于0");
}
if (amount > balance_) {
throw std::runtime_error("余额不足");
}
balance_ -= amount;
std::cout << "取款 " << amount << " 成功,余额: " << balance_ << std::endl;
}
void deposit(double amount) {
if (amount <= 0) {
throw std::invalid_argument("存款金额必须大于0");
}
balance_ += amount;
std::cout << "存款 " << amount << " 成功,余额: " << balance_ << std::endl;
}
double getBalance() const { return balance_; }
};
void performBankOperations() {
BankAccount account("123456", 1000.0);
try {
account.deposit(500);
account.withdraw(200);
account.withdraw(2000); // 余额不足
} catch (const std::invalid_argument& e) {
std::cout << "参数错误: " << e.what() << std::endl;
} catch (const std::runtime_error& e) {
std::cout << "运行时错误: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cout << "其他异常: " << e.what() << std::endl;
} catch (...) {
std::cout << "未知异常" << std::endl;
}
}
int main() {
std::cout << "=== 多重catch处理 ===" << std::endl;
performBankOperations();
return 0;
}🎯 自定义异常
创建异常类
cpp
#include <iostream>
#include <stdexcept>
#include <string>
// 自定义异常基类
class FileException : public std::exception {
protected:
std::string message_;
std::string filename_;
public:
FileException(const std::string& message, const std::string& filename)
: message_(message), filename_(filename) {}
const char* what() const noexcept override {
return message_.c_str();
}
const std::string& getFilename() const { return filename_; }
virtual std::string getFullMessage() const {
return message_ + " (文件: " + filename_ + ")";
}
};
// 具体异常类
class FileNotFound : public FileException {
public:
FileNotFound(const std::string& filename)
: FileException("文件未找到", filename) {}
};
class FileReadError : public FileException {
public:
FileReadError(const std::string& filename)
: FileException("文件读取错误", filename) {}
};
class FileWriteError : public FileException {
public:
FileWriteError(const std::string& filename)
: FileException("文件写入错误", filename) {}
};
// 使用自定义异常的类
class FileManager {
public:
static std::string readFile(const std::string& filename) {
if (filename.empty()) {
throw std::invalid_argument("文件名不能为空");
}
if (filename == "nonexistent.txt") {
throw FileNotFound(filename);
}
if (filename == "corrupted.txt") {
throw FileReadError(filename);
}
return "文件内容: " + filename;
}
static void writeFile(const std::string& filename, const std::string& content) {
if (filename.empty()) {
throw std::invalid_argument("文件名不能为空");
}
if (filename == "readonly.txt") {
throw FileWriteError(filename);
}
std::cout << "写入文件 " << filename << ": " << content << std::endl;
}
};
int main() {
std::cout << "=== 自定义异常 ===" << std::endl;
std::vector<std::string> testFiles = {
"normal.txt",
"nonexistent.txt",
"corrupted.txt",
""
};
for (const auto& filename : testFiles) {
try {
std::cout << "\n尝试读取: " << (filename.empty() ? "[空文件名]" : filename) << std::endl;
std::string content = FileManager::readFile(filename);
std::cout << "成功: " << content << std::endl;
} catch (const FileException& e) {
std::cout << "文件异常: " << e.getFullMessage() << std::endl;
} catch (const std::invalid_argument& e) {
std::cout << "参数异常: " << e.what() << std::endl;
}
}
// 测试写入异常
try {
FileManager::writeFile("readonly.txt", "测试内容");
} catch (const FileWriteError& e) {
std::cout << "\n写入异常: " << e.getFullMessage() << std::endl;
}
return 0;
}🔄 异常安全性
RAII和异常安全
cpp
#include <iostream>
#include <memory>
#include <stdexcept>
// RAII资源管理类
class Resource {
private:
std::string name_;
int* data_;
public:
explicit Resource(const std::string& name) : name_(name) {
data_ = new int[100];
std::cout << "资源 " << name_ << " 已分配" << std::endl;
}
~Resource() {
delete[] data_;
std::cout << "资源 " << name_ << " 已释放" << std::endl;
}
// 禁止拷贝
Resource(const Resource&) = delete;
Resource& operator=(const Resource&) = delete;
void doWork() {
std::cout << "资源 " << name_ << " 正在工作" << std::endl;
// 可能抛出异常
if (name_ == "BadResource") {
throw std::runtime_error("工作失败");
}
}
const std::string& getName() const { return name_; }
};
// 异常安全的函数
void safeFunction() {
std::cout << "\n=== 异常安全函数 ===" << std::endl;
Resource res1("Resource1");
Resource res2("Resource2");
try {
res1.doWork();
res2.doWork();
std::cout << "所有资源工作完成" << std::endl;
} catch (const std::exception& e) {
std::cout << "异常: " << e.what() << std::endl;
// 资源会自动释放(RAII)
}
// res1和res2在这里自动析构
}
// 不安全的函数(演示)
void unsafeFunction() {
std::cout << "\n=== 不安全函数(演示) ===" << std::endl;
int* data1 = new int[100];
int* data2 = new int[100];
try {
std::cout << "分配了两个数组" << std::endl;
// 如果这里抛出异常,内存会泄漏
throw std::runtime_error("模拟异常");
delete[] data1; // 不会执行
delete[] data2; // 不会执行
} catch (const std::exception& e) {
std::cout << "异常: " << e.what() << std::endl;
// 内存泄漏!
delete[] data1; // 手动清理
delete[] data2;
}
}
// 使用智能指针的安全函数
void smartPointerFunction() {
std::cout << "\n=== 智能指针安全函数 ===" << std::endl;
auto res1 = std::make_unique<Resource>("SmartResource1");
auto res2 = std::make_unique<Resource>("SmartResource2");
try {
res1->doWork();
res2->doWork();
std::cout << "智能指针管理的资源工作完成" << std::endl;
} catch (const std::exception& e) {
std::cout << "异常: " << e.what() << std::endl;
// 智能指针自动释放资源
}
}
int main() {
std::cout << "=== 异常安全性 ===" << std::endl;
safeFunction();
unsafeFunction();
smartPointerFunction();
return 0;
}📋 异常处理最佳实践
异常处理指导原则
cpp
#include <iostream>
#include <stdexcept>
#include <vector>
class BestPracticesDemo {
public:
// 1. 在构造函数中使用异常
class SafeArray {
private:
std::vector<int> data_;
public:
SafeArray(size_t size) {
if (size == 0) {
throw std::invalid_argument("数组大小必须大于0");
}
if (size > 1000000) {
throw std::overflow_error("数组太大");
}
data_.resize(size);
std::cout << "创建大小为 " << size << " 的数组" << std::endl;
}
int& at(size_t index) {
if (index >= data_.size()) {
throw std::out_of_range("索引越界");
}
return data_[index];
}
size_t size() const noexcept { return data_.size(); }
};
// 2. noexcept规范
static int add(int a, int b) noexcept {
return a + b; // 保证不抛异常
}
static void printMessage(const std::string& msg) noexcept {
try {
std::cout << msg << std::endl;
} catch (...) {
// 吞掉所有异常,保证noexcept
}
}
// 3. 异常规范的使用
static void demonstrateExceptionSafety() {
std::cout << "=== 异常安全等级 ===" << std::endl;
try {
SafeArray arr(10);
// 基本保证:如果异常发生,对象处于有效状态
arr.at(5) = 42;
// 强异常保证:要么完全成功,要么完全回滚
std::cout << "数组操作成功" << std::endl;
// 尝试访问无效索引
arr.at(15) = 100; // 会抛出异常
} catch (const std::out_of_range& e) {
std::cout << "边界异常: " << e.what() << std::endl;
} catch (const std::exception& e) {
std::cout << "其他异常: " << e.what() << std::endl;
}
}
};
int main() {
std::cout << "=== 异常处理最佳实践 ===" << std::endl;
// 测试构造函数异常
try {
BestPracticesDemo::SafeArray invalidArray(0);
} catch (const std::exception& e) {
std::cout << "构造异常: " << e.what() << std::endl;
}
// noexcept函数调用
int result = BestPracticesDemo::add(3, 4);
std::cout << "noexcept加法: " << result << std::endl;
BestPracticesDemo::printMessage("这是noexcept消息");
// 异常安全演示
BestPracticesDemo::demonstrateExceptionSafety();
std::cout << "\n=== 异常处理原则 ===" << std::endl;
std::cout << "1. 使用RAII管理资源" << std::endl;
std::cout << "2. 优先使用标准异常类" << std::endl;
std::cout << "3. 不要在析构函数中抛异常" << std::endl;
std::cout << "4. 适当使用noexcept规范" << std::endl;
std::cout << "5. 异常信息要清晰明确" << std::endl;
return 0;
}总结
异常处理是C++程序健壮性的重要保障:
异常类型层次
- std::exception: 基础异常类
- std::logic_error: 逻辑错误
- std::runtime_error: 运行时错误
- 自定义异常: 特定领域异常
异常安全等级
| 等级 | 说明 | 特点 |
|---|---|---|
| 无保证 | 可能产生资源泄漏 | 不推荐 |
| 基本保证 | 对象处于有效状态 | 最低要求 |
| 强保证 | 要么成功要么回滚 | 推荐 |
| 不抛异常 | 保证不抛出异常 | 最高级别 |
最佳实践
- 使用RAII管理资源
- 合理设计异常层次
- 适当使用noexcept
- 避免在析构函数中抛异常
- 提供清晰的异常信息
异常处理让程序能够优雅地处理错误情况,提高代码的可靠性和用户体验。