乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • C++ 学习路径
  • 第1章 C++概述与开发环境

    • C++ 语言概述与编译模型
    • 第一个 C++ 程序与规范
    • 开发环境配置
    • 预处理指令详解
  • 第2章 基本语法与类型系统

    • 变量与基本类型
    • 类型转换
    • 枚举类型
    • 引用与指针
    • 数组与 stdarray
    • 字符串与原始字符串字面量
    • const 与 constexpr
    • nullptr 与空指针
    • 类型推导 auto 与 decltype
    • 基于范围的 for 循环
    • 值类别全面解析
    • char16_t 与 char32_t
    • static_assert 编译期断言
  • 第3章 函数与重载

    • 函数声明与定义
    • 函数重载
    • 默认参数与内联函数
    • Lambda 表达式
    • 函数对象与 stdfunction
    • 后置返回类型与 noexcept
  • 第4章 类与对象

    • 类的基本定义
    • 构造函数与析构函数
    • 拷贝控制
    • 移动构造函数与移动赋值
    • 列表初始化与类内初始化器
    • 静态成员与嵌套类
    • 友元
    • =default 与 =delete
  • 第5章 继承与多态

    • 继承基础
    • 虚函数与多态
    • 虚函数表与动态绑定原理
    • 虚析构函数
    • 抽象类与纯虚函数
    • 多重继承与虚继承
    • 继承构造函数
  • 第6章 运算符重载

    • 运算符重载基础
    • 算术与关系运算符重载
    • 赋值与移动运算符重载
    • 特殊运算符重载
  • 第7章 模板与泛型编程

    • 函数模板
    • 类模板
    • 模板特化与偏特化
    • 可变参数模板
    • 别名模板与模板模板参数
    • SFINAE 与类型萃取
    • 依赖名与 typename/template 关键字
  • 第8章 异常处理

    • 异常处理机制
    • noexcept 与异常安全
    • RAII 与异常安全实践
  • 第9章 内存管理与智能指针

    • 动态内存与内存分区
    • RAII 惯用法
    • unique_ptr
    • shared_ptr 与 weak_ptr
    • 内存管理最佳实践
  • 第10章 右值引用与移动语义

    • 右值引用与值类别深度解析
    • std::move 原理与使用
    • 完美转发与 std::forward
    • 移动语义性能对比与最佳实践
  • 第11章 STL容器

    • vector 深度剖析
    • deque 内部机制
    • list 与 forward_list
    • map 与 set 深度解析
    • unordered 容器与哈希原理
    • array 与 tuple
    • 容器适配器
    • 容器选择全景指南
  • 第12章 STL算法与迭代器

    • 迭代器体系全解
    • 非变异算法
    • 变异算法
    • 排序与二分算法
    • Lambda 与算法组合
    • std::random 随机数库
    • 自定义迭代器开发
  • 第13章 IO流与文件

    • 标准 IO 流
    • 格式化输出控制
    • 文件流操作
    • 字符串流
    • std::regex 正则表达式
  • 第14章 并发与多线程

    • thread 基础与线程管理
    • mutex 与 lock_guard
    • unique_lock 与 condition_variable
    • thread_local 线程局部存储
    • atomic 与内存序
    • future 与 async 异步编程
    • std::chrono 时间库
  • 第15章 现代C++新特性

    • 从 C++11 到 C++20 演进路线
    • C++14 关键新特性
    • C++17 关键新特性
    • C++20 核心特性速览
  • 第16章 面试考点与最佳实践

    • C++ 综合最佳实践清单
    • 高频面试题精讲
    • 多线程面试题与实战
    • 内存管理常见陷阱与排查
  • 附录

    • C++ 核心知识点
    • C++ 专业术语

RAII 惯用法

定义与作用

RAII(Resource Acquisition Is Initialization)是 C++ 中最重要的设计惯用法:在对象构造时获取资源,在析构时释放资源,利用 C++ 的确定性析构保证资源在任何退出路径(含异常)下都被正确释放。

{
    std::lock_guard<std::mutex> lock(mtx);  // 构造 → 加锁
    // 临界区
    // 离开作用域 → lock 析构 → 自动解锁
}

核心原理

RAII 与异常安全的统一

完整示例

示例一:数据库事务的 RAII 管理

场景说明:模拟一个数据库事务——RAII 确保事务要么提交要么回滚。

#include <iostream>
#include <string>
#include <stdexcept>
#include <vector>
#include <functional>

class Database {
public:
    void begin_transaction() {
        std::cout << "  BEGIN TRANSACTION" << std::endl;
    }
    void commit() {
        std::cout << "  COMMIT" << std::endl;
    }
    void rollback() {
        std::cout << "  ROLLBACK" << std::endl;
    }
    void execute(const std::string& sql) {
        std::cout << "  SQL: " << sql << std::endl;
    }
};

// RAII 事务守卫
class TransactionGuard {
public:
    explicit TransactionGuard(Database& db) : db_(db), committed_(false) {
        db_.begin_transaction();
    }

    ~TransactionGuard() {
        if (!committed_) {
            db_.rollback();
        }
    }

    void commit() {
        db_.commit();
        committed_ = true;
    }

    TransactionGuard(const TransactionGuard&) = delete;
    TransactionGuard& operator=(const TransactionGuard&) = delete;

private:
    Database& db_;
    bool committed_;
};

// 业务操作
void transfer_money(Database& db, const std::string& from,
                    const std::string& to, double amount) {
    TransactionGuard txn(db);  // RAII:自动 begin

    if (amount <= 0)
        throw std::invalid_argument("转账金额无效");

    db.execute("UPDATE accounts SET balance = balance - "
              + std::to_string(amount) + " WHERE name = '" + from + "'");
    db.execute("UPDATE accounts SET balance = balance + "
              + std::to_string(amount) + " WHERE name = '" + to + "'");

    txn.commit();  // 所有操作成功 → 提交
}

int main() {
    Database db;

    std::cout << "===== 成功转账 =====" << std::endl;
    try {
        transfer_money(db, "Alice", "Bob", 1000);
        std::cout << "转账成功!" << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "转账失败: " << e.what() << std::endl;
    }

    std::cout << "\n===== 失败转账(自动回滚)=====" << std::endl;
    try {
        transfer_money(db, "Bob", "Charlie", -500);
    } catch (const std::exception& e) {
        std::cerr << "转账失败: " << e.what() << std::endl;
        std::cout << "(TransactionGuard 自动 rollback)" << std::endl;
    }

    return 0;
}

预期输出:

===== 成功转账 =====
  BEGIN TRANSACTION
  SQL: UPDATE accounts SET balance = balance - 1000.000000 WHERE name = 'Alice'
  SQL: UPDATE accounts SET balance = balance + 1000.000000 WHERE name = 'Bob'
  COMMIT
转账成功!

===== 失败转账(自动回滚)=====
  BEGIN TRANSACTION
  ROLLBACK
转账失败: 转账金额无效
(TransactionGuard 自动 rollback)

示例二:多种资源的 RAII 组合

#include <iostream>
#include <fstream>
#include <mutex>
#include <memory>
#include <string>

// 通用 ScopeGuard
class ScopeGuard {
public:
    template <typename Func>
    ScopeGuard(Func&& f) : on_exit_(std::forward<Func>(f)) {}

    ~ScopeGuard() { on_exit_(); }

    ScopeGuard(const ScopeGuard&) = delete;
    ScopeGuard& operator=(const ScopeGuard&) = delete;

private:
    std::function<void()> on_exit_;
};

// 日志系统:组合文件 + 互斥锁 RAII
class ThreadSafeLogger {
public:
    explicit ThreadSafeLogger(const std::string& path) {
        file_.open(path, std::ios::app);
        if (!file_) throw std::runtime_error("无法打开日志文件");
    }

    void log(const std::string& msg) {
        std::lock_guard<std::mutex> lock(mutex_);  // RAII 锁
        file_ << msg << std::endl;
    }

private:
    std::ofstream file_;   // RAII 文件
    std::mutex mutex_;     // RAII 锁(通过 lock_guard)
};

int main() {
    std::cout << "===== RAII 组合 =====" << std::endl;

    {
        ThreadSafeLogger logger("app.log");
        logger.log("系统启动");
        logger.log("处理请求...");

        // ScopeGuard
        auto guard = ScopeGuard([] {
            std::cout << "  [ScopeGuard] 退出前清理" << std::endl;
        });

        std::cout << "  业务处理中..." << std::endl;
    }
    std::cout << "  logger 和 guard 都已析构" << std::endl;

    return 0;
}

预期输出:

===== RAII 组合 =====
  业务处理中...
  [ScopeGuard] 退出前清理
  logger 和 guard 都已析构

易错场景与面试考点

场景错误正确
裸 new/delete异常时泄漏用 unique_ptr/shared_ptr
手动 lock/unlock异常时不 unlocklock_guard/unique_lock
析构函数抛异常terminate析构函数 noexcept

常见面试问题

  1. RAII 是什么?为什么是 C++ 的核心惯用法?——构造获取资源、析构释放资源,利用确定性析构保证异常安全。
  2. C++ 标准库中哪些地方使用了 RAII?——lock_guard、unique_ptr、fstream、jthread。
  3. RAII 和 GC 的区别?——RAII 确定性、管理所有资源类型;GC 只管理内存、时机不确定。

小结

  • RAII:构造获取 → 析构释放 → 确定性资源管理
  • 覆盖所有资源类型:内存、文件、锁、线程、事务
  • 异常安全的根基——析构函数在栈展开时一定被调用
上一页
动态内存与内存分区
下一页
unique_ptr