C++异常处理与错误管理之构建稳定可靠的程序
作者:星河耀银海
这篇文章给大家介绍了C++异常处理与错误管理机制,涵盖从基础语法到高级应用,,文章强调异常处理应遵循"只处理真正异常"的原则,感兴趣的朋友跟随小编一起看看吧
C++异常处理与错误管理:构建稳定可靠的程序

一、学习目标与重点
本章将深入探讨C++异常处理与错误管理的核心机制。通过学习,你将能够:
- 理解异常处理的基本概念,掌握try-catch-finally的使用方法
- 学会抛出和捕获异常,实现程序的错误恢复
- 理解异常类型的继承关系,掌握异常类型的选择与设计
- 熟练使用标准异常类,如std::exception及其派生类
- 掌握异常处理的最佳实践,避免常见的陷阱
- 培养错误管理思维,构建稳定可靠的程序
二、异常处理的基本概念
2.1 异常处理的作用
异常处理是一种程序错误处理机制,它允许程序在发生错误时暂停当前执行流程,跳转到底层的错误处理代码,然后根据需要恢复程序执行。
2.2 异常处理的基本语法
C++异常处理的基本语法包括:
try:包裹可能抛出异常的代码块catch:捕获并处理特定类型的异常throw:抛出异常
#include <iostream>
#include <string>
double divide(double numerator, double denominator) {
if (denominator == 0) {
throw std::string("除数不能为零");
}
return numerator / denominator;
}
int main() {
std::cout << "=== 异常处理基本示例 ===" << std::endl;
try {
double result = divide(10, 0);
std::cout << "结果: " << result << std::endl;
} catch (const std::string& error) {
std::cout << "错误: " << error << std::endl;
}
return 0;
}三、异常类型的选择与设计
3.1 标准异常类
C++标准库提供了一系列标准异常类,它们都继承自std::exception类:
#include <iostream>
#include <stdexcept>
double divide(double numerator, double denominator) {
if (denominator == 0) {
throw std::invalid_argument("除数不能为零");
}
return numerator / denominator;
}
int main() {
std::cout << "=== 标准异常类示例 ===" << std::endl;
try {
double result = divide(10, 0);
std::cout << "结果: " << result << std::endl;
} catch (const std::invalid_argument& error) {
std::cout << "无效参数错误: " << error.what() << std::endl;
} catch (const std::exception& error) {
std::cout << "标准异常: " << error.what() << std::endl;
} catch (...) {
std::cout << "未知异常" << std::endl;
}
return 0;
}3.2 自定义异常类
我们可以继承std::exception类来创建自定义异常类:
#include <iostream>
#include <stdexcept>
#include <string>
class MyException : public std::exception {
private:
std::string message;
public:
MyException(const std::string& msg) : message(msg) {}
virtual const char* what() const noexcept override {
return message.c_str();
}
};
void processData(int value) {
if (value < 0) {
throw MyException("值不能为负数");
}
}
int main() {
std::cout << "=== 自定义异常类示例 ===" << std::endl;
try {
processData(-5);
} catch (const MyException& error) {
std::cout << "自定义异常: " << error.what() << std::endl;
} catch (const std::exception& error) {
std::cout << "标准异常: " << error.what() << std::endl;
}
return 0;
}四、异常处理的最佳实践
4.1 异常处理的原则
- 只在真正需要时使用异常:异常处理的开销较大,应只用于处理预期之外的错误
- 使用合适的异常类型:使用标准异常类或自定义异常类,避免使用基本类型
- 提供足够的错误信息:异常应该包含足够的信息,帮助定位和修复问题
- 及时清理资源:在异常情况下,确保所有资源都能正确释放
- 不要忽略异常:捕获异常后应该处理它,而不是忽略它
4.2 异常处理的常见陷阱
- 异常泄漏:没有正确地捕获和处理异常
- 资源泄漏:在异常情况下没有正确地释放资源
- 异常类型不匹配:捕获的异常类型与抛出的异常类型不匹配
- 异常处理的递归:在异常处理代码中再次抛出异常
#include <iostream>
#include <fstream>
#include <stdexcept>
void readFile(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
throw std::runtime_error("无法打开文件: " + filename);
}
// 读取文件内容
std::string content;
file >> content;
// 处理文件内容
if (content.empty()) {
throw std::runtime_error("文件内容为空: " + filename);
}
std::cout << "文件内容: " << content << std::endl;
}
int main() {
std::cout << "=== 异常处理最佳实践示例 ===" << std::endl;
std::string filename = "nonexistent.txt";
try {
readFile(filename);
} catch (const std::runtime_error& error) {
std::cout << "运行时错误: " << error.what() << std::endl;
} catch (const std::exception& error) {
std::cout << "标准异常: " << error.what() << std::endl;
} catch (...) {
std::cout << "未知异常" << std::endl;
}
return 0;
}五、异常处理的高级应用
5.1 异常处理与资源管理
在C++中,我们可以使用RAII(资源获取即初始化)技术来管理资源,确保在异常情况下资源能够正确释放。
#include <iostream>
#include <fstream>
#include <stdexcept>
class FileHandler {
private:
std::ifstream file;
public:
FileHandler(const std::string& filename) {
file.open(filename);
if (!file.is_open()) {
throw std::runtime_error("无法打开文件: " + filename);
}
}
~FileHandler() {
if (file.is_open()) {
file.close();
}
}
std::string readContent() {
std::string content;
file >> content;
if (content.empty()) {
throw std::runtime_error("文件内容为空");
}
return content;
}
};
void processFile(const std::string& filename) {
FileHandler handler(filename);
std::string content = handler.readContent();
std::cout << "文件内容: " << content << std::endl;
}
int main() {
std::cout << "=== 异常处理与资源管理示例 ===" << std::endl;
std::string filename = "nonexistent.txt";
try {
processFile(filename);
} catch (const std::runtime_error& error) {
std::cout << "运行时错误: " << error.what() << std::endl;
} catch (const std::exception& error) {
std::cout << "标准异常: " << error.what() << std::endl;
}
return 0;
}5.2 异常处理与函数接口
在函数接口中,我们可以使用noexcept关键字来指定函数是否可能抛出异常。
#include <iostream>
#include <stdexcept>
double divide(double numerator, double denominator) noexcept(false) {
if (denominator == 0) {
throw std::invalid_argument("除数不能为零");
}
return numerator / denominator;
}
void processData(int value) noexcept {
// 不会抛出异常的函数
if (value < 0) {
// 虽然有错误,但不抛出异常
std::cerr << "值不能为负数: " << value << std::endl;
return;
}
std::cout << "处理值: " << value << std::endl;
}
int main() {
std::cout << "=== 异常处理与函数接口示例 ===" << std::endl;
try {
double result = divide(10, 0);
std::cout << "结果: " << result << std::endl;
} catch (const std::invalid_argument& error) {
std::cout << "无效参数错误: " << error.what() << std::endl;
}
processData(-5);
processData(10);
return 0;
}六、综合案例:实现一个简单的数据库连接管理器
让我们通过一个综合案例来应用本章所学的知识:
#include <iostream>
#include <string>
#include <stdexcept>
#include <vector>
// 数据库连接类
class DatabaseConnection {
private:
std::string connectionString;
bool isConnected;
public:
DatabaseConnection(const std::string& connStr)
: connectionString(connStr), isConnected(false) {}
~DatabaseConnection() {
if (isConnected) {
disconnect();
}
}
void connect() {
// 模拟连接过程
if (connectionString.empty()) {
throw std::invalid_argument("连接字符串不能为空");
}
// 模拟连接失败
if (connectionString == "invalid") {
throw std::runtime_error("无效的连接字符串");
}
isConnected = true;
std::cout << "成功连接到数据库: " << connectionString << std::endl;
}
void disconnect() {
if (!isConnected) {
return;
}
isConnected = false;
std::cout << "断开与数据库的连接: " << connectionString << std::endl;
}
std::vector<std::string> query(const std::string& sql) {
if (!isConnected) {
throw std::runtime_error("数据库未连接");
}
if (sql.empty()) {
throw std::invalid_argument("SQL查询不能为空");
}
// 模拟查询过程
if (sql == "SELECT * FROM users") {
return {"user1", "user2", "user3"};
} else if (sql == "SELECT * FROM products") {
return {"product1", "product2"};
} else {
throw std::runtime_error("未知的SQL查询");
}
}
bool getIsConnected() const {
return isConnected;
}
};
// 数据库操作类
class DatabaseOperations {
private:
DatabaseConnection connection;
public:
DatabaseOperations(const std::string& connStr)
: connection(connStr) {}
void initialize() {
connection.connect();
}
void executeQuery(const std::string& sql) {
try {
std::vector<std::string> results = connection.query(sql);
std::cout << "查询结果: ";
for (const std::string& result : results) {
std::cout << result << " ";
}
std::cout << std::endl;
} catch (const std::invalid_argument& error) {
std::cout << "无效参数错误: " << error.what() << std::endl;
throw; // 重新抛出异常
} catch (const std::runtime_error& error) {
std::cout << "运行时错误: " << error.what() << std::endl;
throw; // 重新抛出异常
}
}
};
int main() {
std::cout << "=== 数据库连接管理器示例 ===" << std::endl;
try {
DatabaseOperations dbOps("valid_connection_string");
dbOps.initialize();
dbOps.executeQuery("SELECT * FROM users");
dbOps.executeQuery("SELECT * FROM products");
} catch (const std::exception& error) {
std::cout << "程序异常: " << error.what() << std::endl;
return 1;
}
return 0;
}七、总结与练习
7.1 本章总结
本章介绍了C++异常处理与错误管理的核心知识,包括:
- 异常处理的基本概念
- 异常类型的选择与设计
- 异常处理的最佳实践
- 异常处理的高级应用
- 综合案例:实现一个简单的数据库连接管理器
7.2 练习题
- 写一个程序,使用异常处理实现一个简单的除法计算器。
- 编写一个程序,使用自定义异常类处理文件读写错误。
- 写一个程序,使用RAII技术管理资源。
- 实现一个函数,使用noexcept关键字指定是否可能抛出异常。
- 写一个程序,使用综合异常处理技术实现一个简单的网络请求管理器。
7.3 进阶挑战
- 研究C++中的异常传播机制,实现跨函数的异常传播。
- 学习如何使用C++的异常类型萃取(Exception Type Traits)。
- 研究C++中的异常处理性能,优化程序的异常处理代码。
- 学习如何使用C++的并发编程特性,实现多线程的异常处理。
到此这篇关于C++异常处理与错误管理之构建稳定可靠的程序的文章就介绍到这了,更多相关C++异常处理与错误管理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
