Exception异常处理实战案例

C++异常处理通过try/throw/catch机制分离错误检测与处理。核心内容包括:自定义异常类设计、标准库异常应用、RAII资源管理、跨函数异常传递、多线程协同处理、大型项目异常策略、调试优化技巧等。通过实战案例展示从基础到综合应用的完整流程,提升代码健壮性。

第一章:异常处理基础与核心机制

C++异常处理通过try、throw和catch机制实现错误管理,其核心是分离异常检测与处理,提升代码健壮性。try块包裹可能抛出异常的代码,throw触发异常,catch捕获并处理特定类型异常。例如,除法运算中检测分母为零时抛出异常,避免程序崩溃。栈展开机制确保异常沿调用链向上传递,直至匹配的catch块处理。标准库提供std::exception基类,支持自定义异常类型,通过what()方法返回错误信息。理解异常处理流程是实战的基础,为后续章节的复杂场景铺垫。

#include <iostream>
#include <stdexcept>

double safeDivide(double numerator, double denominator) {
    if (denominator == 0) {
        throw std::runtime_error("除数不能为零");
    }
    return numerator / denominator;
}

int main() {
    try {
        double result = safeDivide(10, 0);
        std::cout << "结果: " << result << std::endl;
    } catch (const std::runtime_error& e) {
        std::cerr << "捕获异常: " << e.what() << std::endl;
    }
    return 0;
}


第二章:自定义异常类的设计与应用

自定义异常类继承std::exception,可扩展错误信息。例如,定义InvalidInput类处理非法输入,重写what()方法返回详细描述。实战中,自定义异常需遵循单一职责原则,避免过度泛化。通过throw抛出对象实例,catch按类型匹配处理。案例:文件操作中,若读取失败抛出FileError,捕获后提示用户。自定义异常提升代码可读性,便于调试和维护。


#include <iostream>
#include <string>
#include <stdexcept>

class FileError : public std::exception {
private:
    std::string message;
public:
    explicit FileError(const std::string& msg) : message(msg) {}
    const char* what() const noexcept override {
        return message.c_str();
    }
};

void readFile(const std::string& filename) {
    // 模拟文件读取失败
    throw FileError("无法打开文件: " + filename);
}

int main() {
    try {
        readFile("nonexistent.txt");
    } catch (const FileError& e) {
        std::cerr << "文件错误: " << e.what() << std::endl;
    }
    return 0;
}

第三章:标准库异常与常见场景

C++标准库提供std::runtime_error、std::logic_error等预定义异常,覆盖运行时错误(如内存不足)和逻辑错误(如参数无效)。例如,std::out_of_range处理数组越界。实战中,优先使用标准异常以减少冗余代码。案例:数学运算中,std::domain_error处理无效输入。标准异常简化错误管理,确保跨平台兼容性。

#include <iostream>
#include <vector>
#include <stdexcept>

void accessVector(const std::vector<int>& vec, size_t index) {
    if (index >= vec.size()) {
        throw std::out_of_range("索引 " + std::to_string(index) + 
                               " 超出向量范围 [0, " + 
                               std::to_string(vec.size()-1) + "]");
    }
    std::cout << "元素值: " << vec[index] << std::endl;
}

int main() {
    std::vector<int> numbers = {1, 2, 3};
    try {
        accessVector(numbers, 5); // 越界访问
    } catch (const std::out_of_range& e) {
        std::cerr << "范围错误: " << e.what() << std::endl;
    }
    return 0;
}


第四章:异常处理中的资源管理

异常可能导致资源泄漏,需结合RAII(资源获取即初始化)机制。例如,使用智能指针管理动态内存,析构时自动释放。try块内分配资源,catch捕获异常后释放。案例:文件操作中,std::ifstream对象在try块内打开文件,异常时析构句柄。RAII确保资源安全,避免手动清理的遗漏。


#include <iostream>
#include <memory>
#include <fstream>

class ResourceManager {
private:
    std::unique_ptr<int[]> data;
public:
    ResourceManager(size_t size) : data(std::make_unique<int[]>(size)) {
        std::cout << "资源分配: " << size << " 个整数" << std::endl;
    }
    ~ResourceManager() {
        std::cout << "资源自动释放" << std::endl;
    }
};

void processWithRAII() {
    ResourceManager manager(100); // RAII管理资源
    throw std::runtime_error("模拟处理过程中的异常");
    // 异常发生时,manager会自动析构释放资源
}

int main() {
    try {
        processWithRAII();
    } catch (const std::exception& e) {
        std::cerr << "异常: " << e.what() << std::endl;
    }
    return 0;
}

第五章:跨函数异常传递与处理

异常沿调用链向上传递,调用者需处理或继续传递。例如,函数A调用B,B抛出异常时,A的catch块处理。若A不处理,异常继续向上。实战中,声明noexcept标记函数不抛出异常,优化性能。案例:库函数声明noexcept,避免调用者额外开销。跨函数传递需明确职责边界。

#include <iostream>

void functionC() {
    throw std::runtime_error("来自深层函数的异常");
}

void functionB() {
    functionC(); // 不处理异常,继续向上传递
}

void functionA() {
    try {
        functionB();
    } catch (const std::exception& e) {
        std::cerr << "在functionA中捕获: " << e.what() << std::endl;
        // 可以选择重新抛出
        throw;
    }
}

int main() {
    try {
        functionA();
    } catch (const std::exception& e) {
        std::cerr << "在main中最终处理: " << e.what() << std::endl;
    }
    return 0;
}


第六章:异常处理与多线程协同

多线程环境中,异常需在创建线程的上下文中处理。例如,std::thread的join()可能抛出异常,需在主线程catch块处理。实战中,避免线程间直接传递异常,改用std::promise或信号量同步。案例:生产者-消费者模型中,异常通过共享变量传递,确保线程安全。多线程异常处理需谨慎设计。

#include <iostream>
#include <thread>
#include <future>
#include <stdexcept>

int computeTask(int value) {
    if (value < 0) {
        throw std::invalid_argument("输入值不能为负");
    }
    return value * value;
}

int main() {
    auto future = std::async(std::launch::async, computeTask, -5);
    
    try {
        int result = future.get();
        std::cout << "计算结果: " << result << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "线程异常: " << e.what() << std::endl;
    }
    return 0;
}


第七章:大型项目中的异常策略

大型项目需统一异常策略,如分层处理:底层函数抛出异常,高层捕获并转换。例如,数据库操作抛出DBError,业务层捕获后转为UserFriendlyError。实战中,定义异常基类BaseException,子类继承扩展。案例:微服务架构中,异常通过HTTP状态码传递,提升可维护性。统一策略减少耦合。

#include <iostream>
#include <string>

class BaseException : public std::exception {
protected:
    std::string context;
public:
    BaseException(const std::string& ctx) : context(ctx) {}
    virtual std::string getContext() const { return context; }
};

class DBError : public BaseException {
public:
    explicit DBError(const std::string& ctx) : BaseException(ctx) {}
    const char* what() const noexcept override {
        return "数据库操作异常";
    }
};

class UserFriendlyError : public BaseException {
public:
    explicit UserFriendlyError(const std::string& ctx) : BaseException(ctx) {}
    const char* what() const noexcept override {
        return "系统繁忙,请稍后重试";
    }
};

void databaseOperation() {
    throw DBError("连接数据库失败");
}

void businessLayer() {
    try {
        databaseOperation();
    } catch (const DBError& e) {
        throw UserFriendlyError(e.getContext());
    }
}

int main() {
    try {
        businessLayer();
    } catch (const UserFriendlyError& e) {
        std::cerr << "用户友好提示: " << e.what() << std::endl;
    }
    return 0;
}


第八章:异常调试与性能优化

异常调试需结合日志和断点。例如,try块内记录关键状态,catch块输出错误上下文。性能优化方面,避免频繁抛出异常,改用错误码处理高频场景。案例:游戏引擎中,物理运算使用错误码,异常仅处理致命错误。调试工具如GDB可捕获异常栈,定位问题根源。


#include <iostream>
#include <chrono>
#include <stdexcept>

class PerformanceMonitor {
private:
    std::chrono::steady_clock::time_point start;
public:
    PerformanceMonitor() : start(std::chrono::steady_clock::now()) {}
    ~PerformanceMonitor() {
        auto end = std::chrono::steady_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
        std::cout << "操作耗时: " << duration.count() << " 微秒" << std::endl;
    }
};

// 高频操作使用错误码
bool fastOperation(int value, int& result) {
    if (value < 0) return false; // 错误码方式
    result = value * 2;
    return true;
}

// 关键操作使用异常
void criticalOperation() {
    PerformanceMonitor monitor;
    throw std::runtime_error("关键业务异常");
}

int main() {
    // 高频场景:错误码
    int result;
    if (!fastOperation(-5, result)) {
        std::cerr << "操作失败" << std::endl;
    }
    
    // 关键场景:异常
    try {
        criticalOperation();
    } catch (const std::exception& e) {
        std::cerr << "关键异常: " << e.what() << std::endl;
    }
    return 0;
}

第九章:异常处理代码维护

良好异常设计有利于代码维护。例如,异常信息包含上下文(如文件名、行号),便于追踪。实战中,避免catch(…)捕获所有异常,明确类型处理。案例:日志系统中,异常按严重程度分类,便于后续分析。


#include <iostream>
#include <sstream>
#include <stdexcept>

class ContextualException : public std::exception {
private:
    std::string file;
    int line;
    std::string message;
public:
    ContextualException(const std::string& msg, const std::string& f, int l) 
        : message(msg), file(f), line(l) {}
    
    const char* what() const noexcept override {
        std::ostringstream oss;
        oss << "在 " << file << ":" << line << " - " << message;
        static std::string result = oss.str();
        return result.c_str();
    }
};

#define THROW_CONTEXTUAL(msg) \
    throw ContextualException(msg, __FILE__, __LINE__)

void validateInput(int value) {
    if (value > 100) {
        THROW_CONTEXTUAL("输入值超过允许范围");
    }
}

int main() {
    try {
        validateInput(150);
    } catch (const ContextualException& e) {
        std::cerr << "上下文异常: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "其他异常: " << e.what() << std::endl;
    }
    return 0;
}

第十章:实战案例综合演练

综合案例:实现一个文件解析器,处理CSV格式。自定义ParseError异常,try块读取文件,catch捕获std::ios_base::failure(文件不存在)和ParseError(格式错误)。资源管理使用std::unique_ptr,线程安全通过互斥锁保障。案例展示异常处理全流程,从设计到实现,巩固核心概念。

#include <iostream>
#include <fstream>
#include <sstream>
#include <memory>
#include <vector>
#include <stdexcept>
#include <mutex>

class ParseError : public std::exception {
private:
    std::string message;
public:
    explicit ParseError(const std::string& msg) : message(msg) {}
    const char* what() const noexcept override {
        return message.c_str();
    }
};

class CSVParser {
private:
    std::mutex mtx;
public:
    std::vector<std::vector<std::string>> parseFile(const std::string& filename) {
        std::lock_guard<std::mutex> lock(mtx);
        std::ifstream file(filename);
        
        if (!file.is_open()) {
            throw std::ios_base::failure("无法打开文件: " + filename);
        }
        
        std::vector<std::vector<std::string>> data;
        std::string line;
        
        while (std::getline(file, line)) {
            std::vector<std::string> row;
            std::stringstream ss(line);
            std::string cell;
            
            while (std::getline(ss, cell, ',')) {
                if (cell.empty()) {
                    throw ParseError("CSV格式错误: 空单元格");
                }
                row.push_back(cell);
            }
            
            if (row.empty()) {
                throw ParseError("CSV格式错误: 空行");
            }
            data.push_back(row);
        }
        
        if (data.empty()) {
            throw ParseError("CSV文件为空");
        }
        
        return data;
    }
};

int main() {
    CSVParser parser;
    
    try {
        auto data = parser.parseFile("data.csv");
        std::cout << "成功解析 " << data.size() << " 行数据" << std::endl;
    } catch (const std::ios_base::failure& e) {
        std::cerr << "文件错误: " << e.what() << std::endl;
    } catch (const ParseError& e) {
        std::cerr << "解析错误: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "未知错误: " << e.what() << std::endl;
    }
    
    return 0;
}


QT原理与源码分析视频课程
QT QML C++高级扩展开发视频课程
QT QML应用程序性能优化实战视频课程
QT应用程序性能优化实战视频课程

QT&QML原理源码界面美化网络编程(QT5视频课程)
QT&QML性能优化网络编程界面美化(QT6视频课程)
QT C++网络编程系列视频课程
QT+OpenCV+开源框架计算机视觉技术项目实战


如果您认为这篇博客对您有所帮助,请您一定立即点赞+喜欢+收藏,作者将能从您的点赞+喜欢+收藏中获取到创作新的好博客的动力。如果您认为作者写的博客对您有一点帮助,您也可以关注这篇博客的作者。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐