提问者:小点点

C++RAII与延迟?


我最近开始学习C++,以前我是用围棋编程的。

我最近被告知不应该使用new,因为抛出的异常可能会导致分配的内存不是freeD并导致内存泄漏。一个流行的解决方案是RAII,我找到了一个很好的解释为什么要使用RAII以及它在这里是什么。

然而,从Go开始,整个RAII的事情似乎是不必要的复杂。Go有一个叫做defer的东西,它以一种非常直观的方式解决了这个问题。您只需将作用域结束时要执行的操作包装在defer()中,例如defer(free(ptr))defer(close_file(f)),它将自动发生在作用域结束时。

我做了一个搜索,找到了两个源代码,它们试图在这里和这里实现C++中的延迟功能。两个结果都是几乎完全相同的代码,也许其中一个复制了另一个。它们在这里:

推迟实施1:

template <typename F>
struct privDefer {
    F f;
    privDefer(F f) : f(f) {}
    ~privDefer() { f(); }
};

template <typename F>
privDefer<F> defer_func(F f) {
    return privDefer<F>(f);
}

#define DEFER_1(x, y) x##y
#define DEFER_2(x, y) DEFER_1(x, y)
#define DEFER_3(x)    DEFER_2(x, __COUNTER__)
#define defer(code)   auto DEFER_3(_defer_) = defer_func([&](){code;})

推迟实施2:

template <typename F>
struct ScopeExit {
    ScopeExit(F f) : f(f) {}
    ~ScopeExit() { f(); }
    F f;
};

template <typename F>
ScopeExit<F> MakeScopeExit(F f) {
    return ScopeExit<F>(f);
};

#define SCOPE_EXIT(code) \
    auto STRING_JOIN2(scope_exit_, __LINE__) = MakeScopeExit([=](){code;})

我有两个问题:

>

  • 在我看来,这个defer本质上做的是与RAII相同的事情,但更整洁、更直观。有什么区别,您看到使用这些defer实现有什么问题吗?

    我真的不明白#define部分对上面的这些实现做了什么。两者有什么区别,是不是其中一个更可取?


  • 共1个答案

    匿名用户

    你说的很多都是基于观点的,所以我要从我自己的观点开始。

    在C++世界中,我们期望RAII。如果你想和其他开发人员相处得很好,你俩都会遇到这种情况,如果你决定用一种不同的方式来做某事,仅仅因为它是你习惯的,你就会违背标准。

    此外,C++开发人员不使用FOPEN:-)。C++标准库包含了非常好的支持RAII的类,我们使用它们。因此,必须实现RAII实际上意味着在可能的情况下正确选择现有的标准类,或者确保您的对象与RAII兼容。

    我几乎不用重新设计代码来实现RAII。我选择的类会自动处理。

    因此,尽管您所展示的代码很有趣,但它实际上比RAII更复杂。每次你使用FOPEN的时候,你也要记得做你的延迟的事情。使用std::ifstream或std::ofstream是不是更容易?那就已经为你处理好了。(这也适用于您的代码必须在现场实现RAII的其他时候。这已经通过选择正确的类来实现了。)

    所以,不,不是更整洁更直观,因为你要记得去做。选对类,就不用记了。

    至于#definition--它们只是用来确保变量具有唯一的名称,并快捷化defer类的构造函数。

    相关问题


    MySQL Query : SELECT * FROM v9_ask_question WHERE 1=1 AND question regexp '(c++raii|延迟)' ORDER BY qid DESC LIMIT 20
    MySQL Error : Got error 'repetition-operator operand invalid' from regexp
    MySQL Errno : 1139
    Message : Got error 'repetition-operator operand invalid' from regexp
    Need Help?