C++异常处理与错误管理之构建稳定可靠的程序

 更新时间:2026年01月29日 09:04:55   作者:星河耀银海  
这篇文章给大家介绍了C++异常处理与错误管理机制,涵盖从基础语法到高级应用,,文章强调异常处理应遵循"只处理真正异常"的原则,感兴趣的朋友跟随小编一起看看吧

C++异常处理与错误管理:构建稳定可靠的程序

一、学习目标与重点

本章将深入探讨C++异常处理与错误管理的核心机制。通过学习,你将能够:

  1. 理解异常处理的基本概念,掌握try-catch-finally的使用方法
  2. 学会抛出和捕获异常,实现程序的错误恢复
  3. 理解异常类型的继承关系,掌握异常类型的选择与设计
  4. 熟练使用标准异常类,如std::exception及其派生类
  5. 掌握异常处理的最佳实践,避免常见的陷阱
  6. 培养错误管理思维,构建稳定可靠的程序

二、异常处理的基本概念

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 异常处理的原则

  1. 只在真正需要时使用异常:异常处理的开销较大,应只用于处理预期之外的错误
  2. 使用合适的异常类型:使用标准异常类或自定义异常类,避免使用基本类型
  3. 提供足够的错误信息:异常应该包含足够的信息,帮助定位和修复问题
  4. 及时清理资源:在异常情况下,确保所有资源都能正确释放
  5. 不要忽略异常:捕获异常后应该处理它,而不是忽略它

4.2 异常处理的常见陷阱

  1. 异常泄漏:没有正确地捕获和处理异常
  2. 资源泄漏:在异常情况下没有正确地释放资源
  3. 异常类型不匹配:捕获的异常类型与抛出的异常类型不匹配
  4. 异常处理的递归:在异常处理代码中再次抛出异常
#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++异常处理与错误管理的核心知识,包括:

  1. 异常处理的基本概念
  2. 异常类型的选择与设计
  3. 异常处理的最佳实践
  4. 异常处理的高级应用
  5. 综合案例:实现一个简单的数据库连接管理器

7.2 练习题

  1. 写一个程序,使用异常处理实现一个简单的除法计算器。
  2. 编写一个程序,使用自定义异常类处理文件读写错误。
  3. 写一个程序,使用RAII技术管理资源。
  4. 实现一个函数,使用noexcept关键字指定是否可能抛出异常。
  5. 写一个程序,使用综合异常处理技术实现一个简单的网络请求管理器。

7.3 进阶挑战

  1. 研究C++中的异常传播机制,实现跨函数的异常传播。
  2. 学习如何使用C++的异常类型萃取(Exception Type Traits)。
  3. 研究C++中的异常处理性能,优化程序的异常处理代码。
  4. 学习如何使用C++的并发编程特性,实现多线程的异常处理。

到此这篇关于C++异常处理与错误管理之构建稳定可靠的程序的文章就介绍到这了,更多相关C++异常处理与错误管理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言实现控制台五子棋小游戏

    C语言实现控制台五子棋小游戏

    这篇文章主要为大家详细介绍了C语言实现控制台五子棋小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • Visual Studio 2019配置qt开发环境的搭建过程

    Visual Studio 2019配置qt开发环境的搭建过程

    这篇文章主要介绍了Visual Studio 2019配置qt开发环境的搭建过程,本文图文并茂给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-03-03
  • C++实现冒泡排序(BubbleSort)

    C++实现冒泡排序(BubbleSort)

    这篇文章主要为大家详细介绍了C++实现冒泡排序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-04-04
  • c语言 两字符串交叉合并实例

    c语言 两字符串交叉合并实例

    今天小编就为大家分享一篇c语言 两字符串交叉合并实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-12-12
  • C语言数据结构与算法之单链表

    C语言数据结构与算法之单链表

    单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。本文将为大家介绍C语言中单链表的基本概念与读取数据元素,需要的可以参考一下
    2021-12-12
  • QT编写地图实现在线轮廓图的示例代码

    QT编写地图实现在线轮廓图的示例代码

    轮廓图也叫行政区划,这里的轮廓图是指百度地图的区域轮廓图。本文将为大家介绍QT如何实现在线轮廓图的编写,感兴趣的小伙伴可以跟随小编一起学习一下
    2021-12-12
  • C语言中左移和右移运算符详细介绍

    C语言中左移和右移运算符详细介绍

    这篇文章主要介绍了C语言中左移和右移运算符详细介绍的相关资料,需要的朋友可以参考下
    2017-05-05
  • C++中回调函数(CallBack)的用法分析

    C++中回调函数(CallBack)的用法分析

    这篇文章主要介绍了C++中回调函数(CallBack)的用法,较为详细的分析了C++中回调函数(CallBack)的原理并以实例形式总结了其具体用法,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-01-01
  • 提升编程能力的C语言技巧总结

    提升编程能力的C语言技巧总结

    这篇文章主要为大家总结了一些C语言技巧的相关资料,可以帮助大家大大提升编程能力。文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2022-12-12
  • C语言中isdigit()函数和isxdigit()函数的用法

    C语言中isdigit()函数和isxdigit()函数的用法

    这篇文章主要介绍了C语言中isdigit()函数和isxdigit()函数的用法,用来判断字符师傅为阿拉伯数字和16进制数字,需要的朋友可以参考下
    2015-08-08

最新评论