C++RAII与延迟?
我最近开始学习 C++,之前我用 Go 编程。
我最近被告知我不应该使用,new因为抛出的异常可能会导致分配的内存不是freed 并导致内存泄漏。一种流行的解决方案,这是RAII,我发现为什么要使用RAII以及它确实是一个很好的解释在这里。
然而,来自 Go 整个 RAII 的事情似乎不必要地复杂。Go 有一种叫做defer 的东西,它以一种非常直观的方式解决了这个问题。您只需在范围以 结束时包装您想要执行的操作defer(),例如defer(free(ptr))ordefer(close_file(f))并且它会在范围结束时自动发生。
我进行了搜索,发现了两个试图在 C++ 中实现 defer 功能的来源here和here。两者最终都得到了几乎完全相同的代码,也许其中一个复制了另一个。他们来了:
延迟实现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部分在上面的这些实现中做了什么。两者之间有什么区别,其中之一更可取?
回答
你所说的很多都是基于意见的,所以我将从我自己的意见开始。
在 C++ 世界中,我们期待 RAII。如果你想与其他开发人员相处得很好,你们都会遇到它,如果你决定以不同的方式做某事,仅仅因为这是你在 Go 中习惯的东西,你就会违背标准.
此外,C++ 开发人员不使用 FOPEN :-)。C++ 标准库包括非常好的支持 RAII 的类,我们使用它们。因此,必须实现 RAII 实际上意味着在可能的情况下正确选择现有的标准类或确保您的对象与 RAII 兼容。
我几乎不需要重新设计我的代码来实现 RAII。我选择的类会自动处理它。
因此,虽然您展示的代码很有趣,但它实际上比 RAII 需要更多的工作。每次使用 FOPEN 时,您还必须记住做延迟的事情。使用 std::ifstream 或 std::ofstream 不是更容易吗?那么它已经为你处理了。(这可以说是您的代码必须在现场实现 RAII 的其他时候。它已经通过选择正确的类来完成。)
所以,不,它不是更整洁、更直观,因为你必须记住这样做。选择正确的课程,您不必记住。
至于#defines——它们只是为了确保您的变量具有唯一的名称并为 defer 类的构造函数提供快捷方式。
- @Alasdair - you seem to have strong opinions about what you like and don't like. There's nothing wrong with that, but it can hold you back when you try to shift gears and move to a different environment - a different computer language, or library, or whatever. Each has different strengths and weaknesses and philosophies and _attitudes_. C++ has been evolving for over 30 years - faster, more recently - and it has benefited from the experience of building tens of thousands of large (and small, and embedded) systems over that time. My advice: Learn C++, don't just code Go using C++ syntax.