我最近开始学习C++,以前我是用围棋编程的。
我最近被告知不应该使用new
,因为抛出的异常可能会导致分配的内存不是free
D并导致内存泄漏。一个流行的解决方案是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
部分对上面的这些实现做了什么。两者有什么区别,是不是其中一个更可取?
你说的很多都是基于观点的,所以我要从我自己的观点开始。
在C++世界中,我们期望RAII。如果你想和其他开发人员相处得很好,你俩都会遇到这种情况,如果你决定用一种不同的方式来做某事,仅仅因为它是你习惯的,你就会违背标准。
此外,C++开发人员不使用FOPEN:-)。C++标准库包含了非常好的支持RAII的类,我们使用它们。因此,必须实现RAII实际上意味着在可能的情况下正确选择现有的标准类,或者确保您的对象与RAII兼容。
我几乎不用重新设计代码来实现RAII。我选择的类会自动处理。
因此,尽管您所展示的代码很有趣,但它实际上比RAII更复杂。每次你使用FOPEN的时候,你也要记得做你的延迟的事情。使用std::ifstream或std::ofstream是不是更容易?那就已经为你处理好了。(这也适用于您的代码必须在现场实现RAII的其他时候。这已经通过选择正确的类来实现了。)
所以,不,不是更整洁更直观,因为你要记得去做。选对类,就不用记了。
至于#definition--它们只是用来确保变量具有唯一的名称,并快捷化defer类的构造函数。