C&C++快速入门

23、异常

1、课程目标

  • 理解C++异常处理机制
  • 掌握try-catch-throw的使用
  • 理解异常与资源管理
  • 了解Windows SEH异常

2、名词解释

术语 说明
try 异常监视块
catch 异常处理块
throw 抛出异常
exception 标准异常基类
SEH Windows结构化异常处理
RAII 资源获取即初始化
栈展开 异常时自动析构局部对象

3、代码实现

1. 基础异常处理

#include <iostream>
#include <stdexcept>
using namespace std;

double Divide(double a, double b) {
    if (b == 0) {
        throw runtime_error("除数不能为零");
    }
    return a / b;
}

int main() {
    cout << "=== 异常处理 ===" << endl << endl;
    
    try {
        cout << "10 / 2 = " << Divide(10, 2) << endl;
        cout << "10 / 0 = " << Divide(10, 0) << endl;  // 抛出异常
        cout << "这行不会执行" << endl;
    }
    catch (const runtime_error& e) {
        cout << "捕获异常: " << e.what() << endl;
    }
    catch (...) {
        cout << "捕获未知异常" << endl;
    }
    
    cout << "\n程序继续执行" << endl;
    return 0;
}

2. 自定义异常类

#include <iostream>
#include <exception>
using namespace std;

class FileException : public exception {
private:
    string m_msg;
    int m_errorCode;
    
public:
    FileException(const string& msg, int code)
        : m_msg(msg), m_errorCode(code) {}
    
    const char* what() const noexcept override {
        return m_msg.c_str();
    }
    
    int GetErrorCode() const { return m_errorCode; }
};

void OpenFile(const char* filename) {
    // 模拟文件打开失败
    throw FileException("无法打开文件: " + string(filename), 2);
}

int main() {
    cout << "=== 自定义异常 ===" << endl << endl;
    
    try {
        OpenFile("nonexistent.txt");
    }
    catch (const FileException& e) {
        cout << "文件错误: " << e.what() << endl;
        cout << "错误码: " << e.GetErrorCode() << endl;
    }
    
    return 0;
}

3. RAII与异常安全

#include <iostream>
#include <windows.h>
using namespace std;

class FileHandle {
private:
    HANDLE m_handle;
    
public:
    FileHandle(const char* filename, DWORD access) {
        m_handle = CreateFileA(filename, access, 0, NULL,
                               OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (m_handle == INVALID_HANDLE_VALUE) {
            throw runtime_error("无法打开文件");
        }
        cout << "文件已打开" << endl;
    }
    
    ~FileHandle() {
        if (m_handle != INVALID_HANDLE_VALUE) {
            CloseHandle(m_handle);
            cout << "文件已关闭" << endl;
        }
    }
    
    HANDLE Get() const { return m_handle; }
};

void ProcessFile(const char* filename) {
    FileHandle file(filename, GENERIC_READ);  // 如果失败会抛异常
    // 处理文件...
    // 即使这里抛异常,文件也会被正确关闭
}

int main() {
    cout << "=== RAII ===" << endl << endl;
    
    try {
        ProcessFile("C:\\Windows\\System32\\notepad.exe");
    }
    catch (const exception& e) {
        cout << "异常: " << e.what() << endl;
    }
    
    return 0;
}

4. Windows SEH

#include <iostream>
#include <windows.h>
using namespace std;

DWORD FilterException(DWORD code) {
    cout << "异常码: 0x" << hex << code << dec << endl;
    return EXCEPTION_EXECUTE_HANDLER;
}

int main() {
    cout << "=== SEH ===" << endl << endl;
    
    __try {
        int* p = nullptr;
        *p = 42;  // 访问违例
    }
    __except(FilterException(GetExceptionCode())) {
        cout << "捕获SEH异常!" << endl;
    }
    
    cout << "\n程序继续" << endl;
    return 0;
}

5、课后作业

5.1、作业1:实现异常安全的栈

实现一个栈类,在溢出和下溢时抛出异常。

5.2、作业2:练习SEH

使用SEH处理除零异常。