卡码笔记
首页
计算机基础
C++
Java
面经
笔记广场 (opens new window)
代码随想录 (opens new window)
首页
计算机基础
C++
Java
面经
笔记广场 (opens new window)
代码随想录 (opens new window)
  • 基础与语法

  • 面向对象

  • STL 与容器

  • 内存管理

  • C++11 与现代 C++

    • C++11中的新特性有哪些
    • C++11中的多线程编程
    • 左值引用和右值引用的区别
    • 移动语义有什么作用,原理是什么
    • 完美转发的作用及实现
    • 说一下c++中stdmove与stdforward的区别
    • 说一下lambda函数
    • 仿函数与lambda性能对比
    • c++中 STL中仿函数与lambda表达式的性能对比
    • C++中的RAII机制
      • 简要回答
      • 详细回答
      • 代码示例
      • 知识拓展
    • C++中的异常处理机制
    • C++中的协程概念及实现
  • 智能指针

  • 并发与 I/O

# 介绍一下 C++中的RAII机制

面试时没有回答好问题是很正常的,被面试官质疑时,要有今天不会明天会的底气,面后做好复盘,不在同一道题目上跌倒第二次,今天就从 自旋锁与互斥锁的性能对 背起来

# 简要回答

RAII(Resource Acquisition Is Initialization)是C++的核心编程范式,通过对象的构造函数获取资源,析构函数释放资源,确保资源管理的异常安全。

它将资源生命周期与对象生命周期进行绑定,利用C++的栈展开机制保证资源在任何执行路径下都能正确释放。

# 详细回答

RAII机制的核心思想和技术要点:

  • 基本原理:

资源获取在构造函数中完成,资源释放在析构函数中完成,生命周期管理通过对象的作用域控制,异常安全利用栈展开保证析构函数总是被执行

  • 技术优势:

自动管理:无需手动释放资源,避免资源泄漏

异常安全:即使发生异常,资源也能正确释放

代码简洁:资源管理逻辑封装在类内部

作用域控制:资源生命周期清晰明确

  • 关键特性:

利用C++的确定性析构机制,支持所有类型的资源(内存、文件、锁、网络连接等),与STL容器和智能指针深度集成,此外是现代C++资源管理的基石

# 代码示例

文件管理中的RAII

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

class FileHandler {
private:
    std::fstream file;

public:
    // 构造函数获取资源
    FileHandler(const std::string& filename, std::ios_base::openmode mode) {
        file.open(filename, mode);
        if (!file.is_open()) {
            throw std::runtime_error("Failed to open file: " + filename);
        }
        std::cout << "File opened: " << filename << std::endl;
    }

    // 析构函数释放资源
    ~FileHandler() {
        if (file.is_open()) {
            file.close();
            std::cout << "File closed" << std::endl;
        }
    }

    // 禁止拷贝
    FileHandler(const FileHandler&) = delete;
    FileHandler& operator=(const FileHandler&) = delete;

    // 允许移动
    FileHandler(FileHandler&& other) noexcept : file(std::move(other.file)) {}
    FileHandler& operator=(FileHandler&& other) noexcept {
        if (this != &other) {
            file = std::move(other.file);
        }
        return *this;
    }

    // 使用资源
    void write(const std::string& data) {
        file << data;
        if (file.fail()) {
            throw std::runtime_error("Write failed");
        }
    }

    std::string read() {
        std::string content;
        file.seekg(0);
        std::getline(file, content);
        return content;
    }
};

// 使用示例
void use_file_raii() {
    FileHandler file("test.txt", std::ios::out | std::ios::trunc);
    file.write("Hello, RAII!");

    // 文件会在作用域结束时自动关闭,即使发生异常
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

互斥锁的RAII管理

#include <mutex>
#include <thread>
#include <iostream>

class ScopedLock {
private:
    std::mutex& mtx;

public:
    explicit ScopedLock(std::mutex& mutex) : mtx(mutex) {
        mtx.lock();
        std::cout << "Mutex locked" << std::endl;
    }

    ~ScopedLock() {
        mtx.unlock();
        std::cout << "Mutex unlocked" << std::endl;
    }

    // 禁止拷贝和移动
    ScopedLock(const ScopedLock&) = delete;
    ScopedLock& operator=(const ScopedLock&) = delete;
};

std::mutex global_mutex;
int shared_data = 0;

void thread_function(int id) {
    ScopedLock lock(global_mutex);  // 自动上锁
    shared_data++;
    std::cout << "Thread " << id << " updated data to " << shared_data << std::endl;
    // 自动解锁
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 知识拓展

  • 知识图解

image1

image2

  • 适用场景

必须使用RAII的场景:

动态内存管理 - 替代new/delete,避免内存泄漏

文件操作 - 确保文件句柄正确关闭

锁管理 - 避免死锁,保证锁的释放

数据库连接 - 管理连接池,确保连接归还

网络资源 - socket、连接等网络资源管理

图形资源 - OpenGL/DirectX资源管理

事务处理 - 保证事务的原子性提交或回滚

  • 面试官可能追问

Q1: 在RAII类中如何处理拷贝和移动?

A1: 禁止拷贝:对于唯一性资源(文件、锁),应该删除拷贝构造和拷贝赋值

支持移动:通过移动语义转移资源所有权

深拷贝:对于可复制的资源,实现拷贝语义

引用计数:使用shared_ptr管理共享资源

Q2: 如果构造函数中抛出异常,RAII还能正常工作吗?

A2: 能,这正是RAII的强大之处:

如果构造函数中抛出异常,对象构造不算完成

析构函数不会被调用

但已经成功获取的资源会被正确清理

C++保证部分构造的对象会正确清理已完成的成员

Q3: 如何在RAII类中提供资源访问权限?

A3:有几种安全的方式:

提供成员函数:封装资源操作,保持控制

获取原始资源:通过get()方法,但要文档说明所有权

重载运算符:如operator->、operator*等

隐式转换:谨慎使用,可能丧失资源控制

Last Updated: 3/10/2026, 6:08:48 PM

← c++中 STL中仿函数与lambda表达式的性能对比 C++中的异常处理机制 →

评论

验证登录状态...

侧边栏
夜间
卡码简历
代码随想录
卡码投递表🔥
2026群
添加客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记
鼓励/支持/赞赏Carl
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪