聊聊RAII

2023-10-25 17:14:00 浏览数 (1)

原理

RAII(Resource Acquisition Is Initialization),全称资源获取即初始化,1984-1989年期间,比雅尼·斯特劳斯特鲁普和安德鲁·柯尼希在设计C 异常时,为解决资源管理时的异常安全性而使用了该用法,后来比雅尼·斯特劳斯特鲁普将其称为RAII

RAII要求,资源的有效期与持有资源的对象的生命期严格绑定,即由对象的构造函数完成资源的分配(获取),同时由析构函数完成资源的释放。在这种要求下,只要对象能正确地析构,就不会出现资源泄漏问题

我们来看看cppreference上提供的对比案例

未使用RAII的情况

使用RAII的情况

lock_guard的实现如下图所示,在构造函数完成加锁,析构函数中释放锁,并且禁用了拷贝构造函数和赋值运算

由于RAII可以极大地简化资源管理,并有效地保证程序的正确和代码的简洁,所以通常会强烈建议在C 工程中使用它

RAII在Android底层源码的应用更是随处可见,比如SkImageDecoder_libpng中

应用

这里我们来看看FFmpeg Demo开源工程中RAII的应用

在将java层String对象通过JNI传递时,我们通常都会写如下代码

代码语言:javascript复制
// jPath type is jstring
const char *path = env->GetStringUTFChars(jPath, nullptr);

// ....

if (path != nullptr) {
    env->ReleaseStringUTFChars(jPath, path);
}

样板代码写起来繁琐,而且一旦忘记调用相关release接口就会导致内存泄露

在Android源码的libnativehelper下提供了一个基于RAII思想的工具类

ScopedUtfChars的使用方式如下

代码语言:javascript复制
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_xyq_libffplayer_FFPlayer_nativePrepare(JNIEnv *env, jobject thiz, jlong handle,
                                                      jstring path, jobject surface) {
    ATRACE_CALL();
    auto *player = reinterpret_cast<FFMpegPlayer *>(handle);
    ScopedUtfChars scopedPath(env, path);
    std::string s_path = scopedPath.c_str();
    bool result = false;
    if (player) {
        result = player->prepare(env,s_path, surface);
    }
    return result;
}

ScopedUtfChars的实现如下

除了jstring外,在JNI开发中我们经常使用的jXXArray也有类似问题,同样可以使用RAII进行优化

完整代码可以点击文末的"阅读原文"获取

~~END~

0 人点赞