做其他语言的程序员,除了C ++,使用,知道或了解RAII?(Do programmers of o

2019-09-03 11:13发布

我注意到RAII已经越来越#2大量的关注,但在我的圈子(主要是C ++)RAII好像叫什么是类或析构函数那么明显了。

所以我如果那是因为我每天包围,由硬核的C ++程序员,真的很好奇和RAII刚刚是不是众所周知的一般(包括C ++),或者如果#2这一切的质疑是由于这样的事实我在与没有成长起来的C ++程序员,联系方式和其他语言的人,现在我只是不使用/知道RAII?

Answer 1:

对于谁是关于RAII评论在这个线程(资源采集是初始化)的人,这里是一个激励的例子。

class StdioFile {
    FILE* file_;
    std::string mode_;

    static FILE* fcheck(FILE* stream) {
        if (!stream)
            throw std::runtime_error("Cannot open file");
        return stream;
    }

    FILE* fdup() const {
        int dupfd(dup(fileno(file_)));
        if (dupfd == -1)
            throw std::runtime_error("Cannot dup file descriptor");
        return fdopen(dupfd, mode_.c_str());
    }

public:
    StdioFile(char const* name, char const* mode)
        : file_(fcheck(fopen(name, mode))), mode_(mode)
    {
    }

    StdioFile(StdioFile const& rhs)
        : file_(fcheck(rhs.fdup())), mode_(rhs.mode_)
    {
    }

    ~StdioFile()
    {
        fclose(file_);
    }

    StdioFile& operator=(StdioFile const& rhs) {
        FILE* dupstr = fcheck(rhs.fdup());
        if (fclose(file_) == EOF) {
            fclose(dupstr); // XXX ignore failed close
            throw std::runtime_error("Cannot close stream");
        }
        file_ = dupstr;
        return *this;
    }

    int
    read(std::vector<char>& buffer)
    {
        int result(fread(&buffer[0], 1, buffer.size(), file_));
        if (ferror(file_))
            throw std::runtime_error(strerror(errno));
        return result;
    }

    int
    write(std::vector<char> const& buffer)
    {
        int result(fwrite(&buffer[0], 1, buffer.size(), file_));
        if (ferror(file_))
            throw std::runtime_error(strerror(errno));
        return result;
    }
};

int
main(int argc, char** argv)
{
    StdioFile file(argv[1], "r");
    std::vector<char> buffer(1024);
    while (int hasRead = file.read(buffer)) {
        // process hasRead bytes, then shift them off the buffer
    }
}

在此,当StdioFile被创建的实例中,资源(文件流,在这种情况下)被获取; 当它被破坏了,资源被释放。 有没有tryfinally需要的块; 如果读取导致异常, fclose被自动调用,因为它是在析构函数。

析构函数保证时,此功能使被称为main ,是否正常或异常。 在这种情况下,文件流清理。 这个世界是安全的一次。 :-D



Answer 2:

有很多原因RAII是不是更好地了解原因。 首先,这个名字是不是特别明显。 如果我还不知道什么是RAII,我肯定不会从名字就猜出来了。 (资源采集是初始化?那是什么有析构函数或清理,这是真正刻画RAII做什么?)

另一个原因是,它并没有确定的清理在语言正常工作。

在C ++中,我们知道什么时候调用析构函数,我们知道在哪个析构函数被调用的顺序,我们可以定义他们做任何事情,我们喜欢。

在最现代的语言,一切都是垃圾收集,这使得RAII棘手实现。 没有理由为什么它是不可能的添加RAII的扩展,比方说,C#,但它并不那么明显,因为它是在C ++中。 但是,正如其他人所提到的,Perl和其他语言的支持RAII尽管被垃圾收集。

这就是说,它仍然可以创建在C#或其他语言自己的RAII风格的包装。 我这样做是在C#中前一阵子。 我写的东西,以确保数据库连接在使用后立即关闭,其中任何C ++程序员会看到作为RAII一个明显的候选人的任务。 当然,我们可以在包装的一切using ,每当我们使用一个数据库连接-statements,但是这只是杂乱,而且容易出错。

我的解决办法是写历时一个委托作为参数,然后调用时的辅助功能,打开的数据库连接,并且使用语句里面,它传递给委托功能,伪代码:

T RAIIWrapper<T>(Func<DbConnection, T> f){
  using (var db = new DbConnection()){
    return f(db);
  }
}

还不如好的或明显为C ++ - RAII,但它实现了大致相同的事情。 每当我们需要的DbConnection,我们必须调用这保证了它会被关闭之后这个辅助功能。



Answer 3:

我用C ++˚RAII所有的时间,但我也开发了VB6很长一段时间,RAII一直是一个被广泛使用的概念存在(虽然我从来没有听说过有人称呼它)。

事实上,许多VB6程序依赖RAII相当严重。 一,我已经多次看到了更多好奇的用途是下面的小类:

' WaitCursor.cls '
Private m_OldCursor As MousePointerConstants

Public Sub Class_Inititialize()
    m_OldCursor = Screen.MousePointer
    Screen.MousePointer = vbHourGlass
End Sub

Public Sub Class_Terminate()
    Screen.MousePointer = m_OldCursor
End Sub

用法:

Public Sub MyButton_Click()
    Dim WC As New WaitCursor

    ' … Time-consuming operation. '
End Sub

一旦耗时的操作终止时,原始光标被自动恢复。



Answer 4:

RAII代表资源获取就是初始化 。 这不是语言无关的。 这口头禅是在这里,因为C ++的作品它的工作方式。 在C ++对象并不构成直到它的构造完成。 如果对象尚未成功构建的析构函数将不会被调用。

翻译成实用的语言,构造应该确保它涵盖了它不能彻底完成其任务的情况。 如果,例如,施工过程中发生异常,则构造函数必须优雅地处理它,因为析构函数会不会有帮助。 这通常是通过对覆盖在构造函数中的异常或转发此麻烦,对其他对象进行。 例如:

class OhMy {
public:
    OhMy() { p_ = new int[42];  jump(); } 
    ~OhMy() { delete[] p_; }

private:
    int* p_;

    void jump();
};

如果jump()在构造函数中调用抛出我们就麻烦了,因为p_会泄漏。 我们可以解决这个问题是这样的:

class Few {
public:
    Few() : v_(42) { jump(); } 
    ~Few();

private:
    std::vector<int> v_;

    void jump();
};

如果人们没有意识到这一点则是因为两件事情之一:

  • 他们不知道C ++好。 在这种情况下,他们应该打开TCPPPL他们写他们的下一个课前一次。 具体而言,在本书的第三版部分14.4.1关于这项技术的谈判。
  • 他们不知道C ++的。 没关系。 这个成语是非常C ++年。 无论是学习C ++或忘记所有关于这一点,与你的生活矣。 优选地学习C ++。 ;)


Answer 5:

RAII。

它以一个构造函数和析构函数,但它不止于此。
它是所有关于安全的异常的情况下控制资源。

是什么让RAII优于终于和这样的机制是,它使代码更安全的使用,因为它的动作责任正确地使用对象从对象的用户对象的设计师。

读这个

实施例使用StdioFile正确使用RAII。

void someFunc()
{
    StdioFile    file("Plop","r");

    // use file
}
// File closed automatically even if this function exits via an exception.

与最终得到相同的功能。

void someFunc()
{
      // Assuming Java Like syntax;
    StdioFile     file = new StdioFile("Plop","r");
    try
    {
       // use file
    }
    finally
    {
       // close file.
       file.close(); // 
       // Using the finaliser is not enough as we can not garantee when
       // it will be called.
    }
}

因为你必须明确地加入try {}最后{}块,这使得编码的这种方法更容易出错( 即它是一个需要考虑的例外对象的用户)。 通过使用RAII异常安全具有当对象被实施一次代码。

这个问题是这样的C ++具体。
答案很简单:第

更长的答案:
它需要构造器/解析器/异常和具有限定的寿命的对象。

那么技术上它不需要例外。 它只是变得更加有用,当异常可能被使用,因为它使得控制异常很容易存在的资源。
但是,在控制可以留下一个功能早期,而不是执行所有的代码的函数( 如提前返回都非常有用。这就是为什么在碳多重返回点是一个坏的代码味道,而在C ++多重返回点是不是一个代码味道(因为)我们可以清理使用RAII])。

在C ++中控制寿命通过堆栈变量或智能指针来实现的。 但是,这不是我们能有一个严格控制寿命的唯一时间。 例如Perl的对象不栈为基础,但有由于引用计数非常受控的寿命。



Answer 6:

与RAII的问题是缩写。 它没有明显的相关性的概念。 这是什么都与堆栈分配呢? 这就是它归结为。 C ++让你在栈上分配的对象,并保证即使堆栈展开他们的析构函数调用的能力。 在那光,那RAII听起来就像是封装的一种有意义的方式? 不,我从来没有听说过RAII的,直到我几个星期前来到这里,我甚至有,当我读到有人贴过,他们绝不会雇用一个C ++程序员,谁知道不知道什么是RAII是笑很难。 当然这个概念是众所周知的最重要能力的专业C ++开发人员。 这只是缩写,是考虑不周的。



Answer 7:

的修改@皮埃尔的答案 :

在Python:

with open("foo.txt", "w") as f:
    f.write("abc")

f.close()的异常是否提出或不被自动调用。

一般而言,可以做到用contextlib.closing ,从documenation:

closing(thing) :返回该块完成后关闭的事情上下文管理。 这基本上等同于:

 from contextlib import contextmanager @contextmanager def closing(thing): try: yield thing finally: thing.close() 

并让你写这样的代码:

 from __future__ import with_statement # required for python version < 2.6 from contextlib import closing import urllib with closing(urllib.urlopen('http://www.python.org')) as page: for line in page: print line 

而无需显式关闭页面。 即使发生了错误,当与块退出page.close()将被调用。



Answer 8:

Common Lisp的有RAII:

(with-open-file (stream "file.ext" :direction :input)
    (do-something-with-stream stream))

请参阅: http://www.psg.com/~dlamkins/sl/chapter09.html



Answer 9:

首先我很惊讶它没有比较知名的! 我完全没想RAII是,至少,明显的C ++程序员。 但是现在我想我能理解为什么人们实际上询问此事。 我包围,和我自己必须是,C ++怪胎...

所以,我的秘密。我想这将是我用来读取迈尔斯,萨特[编辑:]所有的时间多年前,直到我只是grokked它和安德烈。



Answer 10:

与RAII的事情是,它需要确定性结束的东西是在C ++ stackbased对象保证。 如C#和Java依赖垃圾回收的语言没有这个保障,因此有某种方式被“狂奔”。 在C#中,这是通过实现IDisposable接口完成,几乎相同的使用模式的作物,然后向上basicly这就是激励的“使用”的语句之一,它确保了处理,并很好地了解和使用。

所以basicly成语是存在的,只是没有一个奇特的名字。



Answer 11:

RAII是C ++的方式,以确保清除过程中,无论在代码中会发生什么的代码块后执行:代码执行到最后正确或将引发异常。 一个已被引用的例子是自动关闭文件的处理后,看到答案在这里 。

在其他语言中使用其他机制来实现这一目标。

在Java中,你必须尝试{}终于{}构造:

try {
  BufferedReader file = new BufferedReader(new FileReader("infilename"));
  // do something with file
}
finally {
    file.close();
}

在Ruby中,你有自动挡参数:

File.open("foo.txt") do | file |
  # do something with file
end

在Lisp你有unwind-protect和预定义with-XXX

(with-open-file (file "foo.txt")
  ;; do something with file
)

在流程必须dynamic-wind和预定义with-XXXXX

(with-input-from-file "foo.txt"
  (lambda ()
    ;; do something 
)

在Python你有尝试终于

try
  file = open("foo.txt")
  # do something with file
finally:
  file.close()

C ++的解决方案RAII是,因为它迫使你创建一个类的各种清理你必须做的相当笨拙。 这可能会迫使你写了很多小傻类。

RAII的其他例子是:

  • 解锁后,收购互斥
  • 闭开口后数据库连接
  • 分配后释放内存
  • 登录的代码块的入口和出口
  • ...


Answer 12:

这有点绑知道什么时候你的析构函数会被调用,虽然对不对? 因此,这并不完全是语言无关的,因为这不是一个在许多GC'd语言给出。



Answer 13:

我想了很多其他语言的(那些没有delete ,例如)不给程序员在对象生命周期完全一样的控制,所以必须有其他的手段来提供确定性的处理资源。 在C#,例如,使用usingIDisposable是常见的。



Answer 14:

RAII是流行的C ++,因为它是为数不多的(唯一?)语言可以分配复杂的范围局部变量之一,但没有一个finally条款。 C#,Java和Python和Ruby都有finally或等效。 C具有不finally ,而且当变量下降超出范围不能执行的代码。



Answer 15:

我有同事谁是硬核,“读规范” C ++类型。 他们中许多人知道RAII,但我从来没有真正听到它使用的场景之外。



Answer 16:

CPython的(C编写的官方Python)支持,因为它的使用即时范围基于破坏引用计数对象(而不是回收垃圾时)的RAII。 不幸的是,Jython(在Java中的Python)和PyPy不支持这个非常有用的RAII成语和它打破了很多传统的Python代码。 因此,对于便携式蟒你必须处理所有的异常手动和Java一样。



Answer 17:

RAII是特定于C ++。 C ++有堆栈分配的对象,非托管对象的寿命,和异常处理的必要组合。



文章来源: Do programmers of other languages, besides C++, use, know or understand RAII?