(Translated by https://www.hiragana.jp/)
RAII - 维基百科,自由的百科全书 とべ转到内容ないよう

RAII

本页使用了标题或全文手工转换
维基百科ひゃっか自由じゆうてき百科ひゃっかぜん

RAIIぜんしょう资源获取そくはつはじめ英語えいごResource Acquisition Is Initialization),它是ざいいちめんこう对象语言なかてきいち慣用かんようほうえいProgramming idiom。RAIIげんC++ざいJavaC#DAdaValaRustなか也有やゆう应用。1984-1989ねん间,まさ·斯特ろう斯特魯普安德あんとく鲁·柯尼まれえいAndrew Koenig (programmer)ざい设计C++异常时,为解决資源しげん管理かんりえいResource management (computing)时的異常いじょう安全あんぜんえいException safetyせい而使ようりょう该用ほう[1]きさきらいまさ·斯特ろう斯特魯普はた其称为RAII[2]

RAII要求ようきゅう,资源てき有效ゆうこうあずかゆう资源てき对象てき生命せいめいえいObject lifetime严格绑定,そくよし对象てき构造函数かんすう完成かんせい资源てき分配ぶんぱいえいResource allocation (computer)(获取),どう时由析构函数かんすう完成かんせい资源てき释放。ざい这种要求ようきゅうただよう对象のうせい确地析构,就不かい资源泄漏问题。

作用さよう

[编辑]

RAIIてき主要しゅよう作用さようざいしつだい码简洁性[3]てきどう时,以很こう地保じほ证代码的异常安全あんぜんせい

下面かめんてきC++实例说明りょう如何いかようRAII访问ぶんけん互斥りょう

#include <string>
#include <mutex>
#include <iostream>
#include <fstream>
#include <stdexcept>
 
void write_to_file(const std::string & message)
{
    // 创建关于ぶんけんてき互斥锁
    static std::mutex mutex;
 
    // ざい访问ぶんけんぜん进行
    std::lock_guard<std::mutex> lock(mutex);
 
    // 尝试开文けん
    std::ofstream file("example.txt");
    if (!file.is_open())
        throw std::runtime_error("unable to open file");
 
    // 输出ぶんけん内容ないよう
    file << message << std::endl;
 
    // とう离开作用さよういき时,ぶんけん句柄くがらかいくびさき析构 (かんいなほうりょう异常)
    // 互斥锁也かい析构 (どう样地,かんいなほうりょう异常)
}

C++证了所有しょゆう栈对ぞうざい生命せいめい周期しゅうき结束时会销毁(そく调用析构函数かんすう)[4]所以ゆえん该代码是异常安全あんぜんてき。无论ざいwrite_to_file函数かんすう正常せいじょうかえしかい时,还是ざい途中とちゅうほう异常时,都会とかい引发write_to_file函数かんすうてきうずたか栈回退すさ,而此时会动调ようlockfile对象てき析构函数かんすう

とう一个函数需要通过多个局部变量来管理资源时,RAII就显どく非常ひじょうこのみようよし为只ゆう构造成功せいこう(构造函数かんすうぼつゆうほう异常)てき对象ざいかいざいかえしかい时调よう析构函数かんすう[4]どう时析构函すうてき调用顺序恰好かっこう它们构造顺序てきはんじょ[5],这样すんで以保证多个资げん(对象)てきせい确释放,またのう满足个资げん间的赖关けい

よし于RAII以极大地だいち简化资源管理かんり,并有效ゆうこう地保じほ证程じょてきせい确和だい码的简洁,所以ゆえん通常つうじょうかい强烈きょうれつけん议在C++ちゅう使用しよう它。

典型てんけい用法ようほう

[编辑]

RAIIざいC++ちゅうてき应用非常ひじょう广泛,如C++标准库なかてきlock_guard[6]便びんようRAII方式ほうしきらいひかえせい互斥りょう:

template <class Mutex> class lock_guard {
private:
    Mutex& mutex_;

public:
    lock_guard(Mutex& mutex) : mutex_(mutex) { mutex_.lock(); }
    ~lock_guard() { mutex_.unlock(); }

    lock_guard(lock_guard const&) = delete;
    lock_guard& operator=(lock_guard const&) = delete;
};

ほどじょ员可以非常ひじょう方便ほうべん使用しようlock_guard,而不よう担心异常安全あんぜん问题

extern void unsafe_code();  // 可能かのうほう异常

using std::mutex;
using std::lock_guard;

mutex g_mutex;

void access_critical_section()
{
    lock_guard<mutex> lock(g_mutex);
    unsafe_code();
}

实际じょうC++标准库てき实现就广泛应ようりょうRAII,典型てんけいてき容器ようき智能ちのうゆびひとし

RRID

[编辑]

RAII还有另外一种被称为RRID(Resource Release Is Destruction)てき特殊とくしゅ用法ようほう[7]そくざい构造时没ゆう“获取”资源,ただしざい析构时释放资源。ScopeGuard[8]かずBoost.ScopeExit[9]就是RRIDてき典型てんけい应用:

#include <functional>

class ScopeGuard {
private:
    typedef std::function<void()> destructor_type;

    destructor_type destructor_;
    bool dismissed_;

public:
    ScopeGuard(destructor_type destructor) : destructor_(destructor), dismissed_(false) {}

    ~ScopeGuard()
    {
        if (!dismissed_) {
            destructor_();
        }
    }

    void dismiss() { dismissed_ = true; }

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

ScopeGuardどおり常用じょうよう于省いち些不必要ひつようてきRAIIふうそうれい

void foo()
{
    auto fp = fopen("/path/to/file", "w");
    ScopeGuard fp_guard([&fp]() { fclose(fp); });

    write_to_file(fp);                     // 异常安全あんぜん
}

ざいD语言なか,scope关键也是典型てんけいてきRRID用法ようほうれい

void access_critical_section()
{
    Mutex m = new Mutex;
    lock(m); 
    scope(exit) unlock(m);

    unsafe_code();                  // 异常安全あんぜん
}

Resource create()
{
    Resource r = new Resource();
    scope(failure) close(f);

    preprocess(r);                  // ほう异常时会动调ようclose(r)
    return r;
}

あずかfinallyてき比較ひかく

[编辑]

虽然RAIIfinallyのう证资げん管理かんり时的异常安全あんぜんただしあい对来说,使用しようRAIIてきだい码相对更简洁。 如まさ·斯特劳斯とく鲁普ところ说,“ざい实环さかいちゅう,调用资源释放だい码的次数じすう远多于资げん类型てき个数,所以ゆえんしょう对于使用しようfinallyらい说,使用しようRAIIのう减少だい码量。”[10]

れい如在Javaちゅう使用しようfinallyらい管理かんりSocket资源

void foo() {
    Socket socket;
    try {
        socket = new Socket();
        access(socket);
    } finally {
        socket.close();
    }
}

ざいさいようRAIIきさきだい码可以简

void foo() {
    try (Socket socket = new Socket()) {
        access(socket);
    }
}

とく别是とう大量たいりょう使用しようSocket时,じゅう复的finally就显とくぼつゆう必要ひつよう

参考さんこう资料

[编辑]