题目

编写程序,阐述现代 C++ 最具革命性的编程哲学——RAII(Resource Acquisition Is Initialization,资源获取即初始化)。封装一个轻量级文件句柄守卫类 FileGuard,在构造函数中通过系统级 fopen 获取系统文件描述符,并在析构函数中自动调用 fclose。证明即使业务逻辑在处理途中提前 return、被 break 中断或抛出运行时异常,栈展开(Stack Unwinding)都能无条件保证文件句柄 100% 绝对不会发生泄漏。

输入格式:无需外部输入。

输出格式:输出正常流程与异常中断触发栈展开时文件资源的自动回收流水日志。

数据范围与约定:RAII 资源生命周期绑定、异常安全与栈展开机制。

思路与算法

1. RAII 的核心思想
将一切系统稀缺资源(堆内存、文件句柄、互斥锁、网络 Socket)的生命周期与栈对象的生命周期强绑定。资源的获取发生在构造函数中,资源的释放发生在析构函数中。

2. 栈展开(Stack Unwinding)
当 C++ 抛出异常或函数执行完毕离开作用域时,编译器自动沿调用栈逆序回溯,并强制依次调用所有已构造局部栈对象的析构函数。这彻底解决了 C 语言中每个分支都要手写 goto cleanup; 的千古难题。

3. 禁用拷贝语义
资源包装类通常禁止拷贝(= delete),因为系统资源(如文件描述符)一般具有独占排他性。

完整程序

#include <iostream>
#include <cstdio>
#include <stdexcept>

class FileGuard {
private:
    std::FILE* file_;
    std::string filename_;

public:
    explicit FileGuard(const std::string& filename, const char* mode)
        : file_(nullptr), filename_(filename) {
        file_ = std::fopen(filename.c_str(), mode);
        if (!file_) {
            throw std::runtime_error("无法打开目标文件: " + filename_);
        }
        std::cout << "  [RAII 资源获取] 成功打开文件句柄: " << filename_ << "\n";
    }

    // 禁用拷贝构造与拷贝赋值,防止句柄被重复关闭
    FileGuard(const FileGuard&) = delete;
    FileGuard& operator=(const FileGuard&) = delete;

    ~FileGuard() {
        if (file_) {
            std::fclose(file_);
            std::cout << "  [RAII 自动析构] 无论何种路径退出,文件句柄已安全自动关闭: " 
                      << filename_ << "\n";
        }
    }

    void writeLine(const std::string& text) {
        if (file_) {
            std::fputs(text.c_str(), file_);
            std::fputc('\n', file_);
        }
    }
};

void simulateWorkWithException() {
    FileGuard file("app_temp.log", "w");
    file.writeLine("第一行:系统初始化记录");
    
    std::cout << "  [业务逻辑] 模拟业务途中突发致命运行时异常...\n";
    throw std::runtime_error("数据库连接超时异常!");
    
    // 下方代码因异常无法到达
    file.writeLine("第二行:该行绝不会被写入");
}

int main() {
    std::cout << "=== 测试 RAII 异常安全保证与栈展开机制 ===\n";
    try {
        simulateWorkWithException();
    } catch (const std::exception& e) {
        std::cout << "外部捕获异常: " << e.what() << "\n";
    }

    std::cout << "\n程序已平稳恢复,且系统底层绝无文件句柄残留泄漏!\n";
    return 0;
}

运行示例

输出:

=== 测试 RAII 异常安全保证与栈展开机制 ===
  [RAII 资源获取] 成功打开文件句柄: app_temp.log
  [业务逻辑] 模拟业务途中突发致命运行时异常...
  [RAII 自动析构] 无论何种路径退出,文件句柄已安全自动关闭: app_temp.log
外部捕获异常: 数据库连接超时异常!

程序已平稳恢复,且系统底层绝无文件句柄残留泄漏!