c语言自动内存回收(RAII实现)

简述

什么是RAII

RAII(Resource Acquisition Is Initialization)是c++之父Bjarne Stroustrup提出的概念。资源一般分三个步骤:获取、使用和销毁,而在自由使用内存的c语言中,资源的销毁常常是程序员容易遗漏的事情,让程序自动销毁资源也成为了业界的常规方案。

c实现

/* 入参是析构函数,在malloc资源时也指定free要使用的函数,尽量不在宏中写函数,减少后续定位的复杂度,并且也满足malloc和free的配对出现,可读性更好 */
#define RAII_FREE(FreeFunc) __attribute__((cleanup(FreeFunc)))

以上代码使用了编译属性__attribute__,在函数退出后自动回收资源,使用方式如下:

  • 示例一:释放内存
void MemFreeL2PointerAndSetNull(void *ptr)
{
    void **pptr = (void **) ptr;
    if (*pptr != NULL)
    {
        free(*pptr);
        *pptr = NULL;
    }
}

void TestFunc()
{
    RAII_FREE(MemFreeL2PointerAndSetNull) TestObj *obj = (TestObj *)malloc(sizeof(TestObj));
    obj->grade = 1;
}
  • 示例二:关闭文件
void CloseFile(void *ptr)
{
    void **pptr = (void **) ptr;
    if (*pptr != NULL)
    {
        (void) fclose(*pptr);
    }
}

void test_whenOpenFile_thenCloseFile(const char *flePath)
{
    RAII_FREE(CloseFile) FILE *handle = fopen(flePath, "r");
    if (!handle)
    {
        printf("open file filed");
    }
}
  • 示例三:释放锁
void ReleaseMutexLock(void *ptr)
{
    void **pptr = (void **) ptr;
    if (*pptr != NULL)
    {
        pthread_mutex_unlock(*pptr);
    }
}

void test_whenLock_thenFree(pthread_mutex_t *lock)
{
    RAII_FREE(ReleaseMutexLock) pthread_mutex_t *mutex = lock;
    pthread_mutex_lock(mutex);
}

理论依据

1、现有的语言c++、rust都有RAII的实现
2、 Linux平台下最常用的C语言函数库 (glib) 中的公共 API (g_autoptr)中使用了该能力
3、A simple defer feature for C(2021)这篇论文也讨论了c的defer关键字,即在函数返回之前执行,原理跟cleanup是一样的

考虑到__attribute__是编译属性,笔者调研了几款常见编译器,支持程度如下:

编译器是否支持参考
GCC支持https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html
Clang支持https://clang.llvm.org/docs/AttributeReference.html
MSVC不支持,可以通过 _try 和 _finally 关键字实现类似的功能https://learn.microsoft.com/en-us/cpp/c-language/try-finally-statement-c?view=msvc-170

优缺点

  • 优点:
    • 宏简单易用,并且没有在宏里定义复杂逻辑,debug和运维都很方便
    • 使代码结构简单画(减少变量/资源清理的代码或者goto跳转)
    • 降低内存泄露、打开文件、锁定互斥锁的风险
  • 缺点:
    • 不完全惯用的c风格
    • 需要依赖编译器来清理

扩展

  • 智能指针:FreeFunc中加入引用技术,则可实现c++中的智能指针功能

参考

RAII in C: cleanup gcc compiler extension

A simple defer feature for C

Add support for GCC-like cleanup attribute for plain C

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
RAII(Resource Acquisition Is Initialization)是一种C++编程技巧,它的主要思想是:在对象的构造函数中获取资源,在析构函数中释放资源。这样可以确保资源的正确释放,即使在发生异常时也不会出错。 实现RAII的一般步骤如下: 1. 创建一个类来管理资源,并在构造函数中获得该资源。例如,如果你需要管理一个文件句柄,你可以创建一个文件句柄类,它的构造函数打开文件并返回一个文件句柄。 2. 在类的析构函数中释放资源。例如,在文件句柄类的析构函数中,你应该关闭文件句柄。 3. 使用这个类创建一个对象,并将其保存在一个自动变量中。这样,在函数的末尾,当自动变量离开作用域时,资源将自动释放。 下面是一个简单的示例,展示如何使用RAII来管理文件句柄: ```c++ #include <iostream> #include <fstream> class File { public: File(const char* filename) : m_file(filename) { std::cout << "File " << filename << " opened.\n"; } ~File() { if (m_file.is_open()) { m_file.close(); std::cout << "File closed.\n"; } } // Example method to write to the file void write(const char* data) { m_file << data; } private: std::ofstream m_file; }; int main() { File file("example.txt"); file.write("Hello, world!"); return 0; } ``` 在上述示例中,我们创建了一个名为File的类,它负责管理文件句柄。在构造函数中打开文件,并在析构函数中关闭文件。在main函数中,我们使用File类来打开文件并写入数据。当程序退出main函数时,File对象离开作用域,析构函数被自动调用,文件句柄被关闭。 这就是RAII的基本思想。通过使用RAII,我们可以确保资源的正确释放,避免了因为异常而导致资源泄漏的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值