Skip to content

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++ 异常处理包括:

  1. 异常的基本概念

    • 异常是程序在运行时发生的错误
    • 使用try-catch块处理异常
  2. 抛出异常

    • 抛出基本类型异常
    • 抛出对象异常
    • 抛出标准异常
  3. 捕获异常

    • 捕获特定类型的异常
    • 捕获多个异常
    • 捕获所有异常
  4. 标准异常

    • std::exception:所有标准异常的基类
    • std::runtime_error:运行时错误
    • std::logic_error:逻辑错误
    • std::invalid_argument:无效参数
    • std::out_of_range:超出范围
  5. 自定义异常

    • 继承std::exception
    • 重写what()方法
  6. 异常规范

    • noexcept关键字:指定函数不会抛出异常
  7. 异常和资源管理

    • RAII(资源获取即初始化)
    • 使用智能指针管理资源

关键概念:

  • 异常:程序在运行时发生的错误
  • 抛出异常:使用throw关键字抛出异常
  • 捕获异常:使用catch块捕获异常
  • 标准异常:C++ 标准库提供的异常类
  • 自定义异常:继承std::exception创建自定义异常
  • RAII:资源获取即初始化,一种资源管理技术

掌握异常处理是编写健壮 C++ 程序的重要基础,在后续章节中,我们将学习 C++ 的动态内存。