我最近刚刚了解了 Scope Guard C++ 习惯用法。不幸的是,我找不到任何好的实现。
谁能指出我在 C++ 中一些好的和可用的 Scope Guard 实现?
谢谢,博达赛多。
最初的 ScopeGuard 类包含在Andrei Alexandrescu 和 Petru Marginean的 Dobb 博士的这篇文章中。稍微改进的版本,与 Joshua Lehrer 的一些更改可在此处获得。(Lehrer 的版本是我在项目中使用的版本。)它也包含在Loki库中。
Boost 现在有一个ScopeExit库,它比 ScopeGuard 更强大(因为它可以执行任意代码,而 ScopeGuard 只能调用一个预先存在的函数)。
编辑:话虽如此,Scope Guard 实际上只是 RAII 的一个特定应用,所以你真的应该至少了解如何实现一个的概念。
ScopeGuard 已包含在Loki库中(在 Andrei Alexandrescu 的 Modern C++ Design 中进行了宣传,我相信您已经听说过这本很棒的书),并且已经足够成熟,可以在生产代码中使用,imo。
明确一点:我们正在讨论使用 RAII 编写异常安全代码。
附加阅读(在 StackOverflow 上): ScopeGuard 的使用真的会带来更好的代码吗?
Folly 库(来自 facebook 的开源)也提供了一个实现(这并不奇怪,因为他们使用了 AA):
https://github.com/facebook/folly/blob/master/folly/ScopeGuard.h
我认为这和这里提到的 MNMLSTC 实现都值得考虑。
让我提供一个基本的 C++20 版本。
#include <concepts>
#include <type_traits>
template <std::invocable Cleanup>
class [[nodiscard]] scope_guard
{
Cleanup d;
public:
scope_guard(Cleanup&& d) : d{std::forward<Cleanup>(d)} {}
scope_guard(const scope_guard&) = delete;
~scope_guard(){d();}
};
// allow construction from plain function
template <typename F>
scope_guard(F&&) -> scope_guard<std::decay_t<F>>;
请注意,除非我们需要移动,否则scope_guard
它会比 callable 增加零内存开销Cleanup
,因为我们不需要以可重置的方式保存它,因为我们不需要移动构造函数,因为我们得到了类模板参数推导。
语言变得多么富有表现力的一个很好的例子。谢谢组委会!
“Scope Guard”对象只是更广泛的RAII习语的一个实例。
并且没有单一的实现。这是 C++ 程序员必须理解的东西,而不仅仅是复制/粘贴。幸运的是,实现起来也很简单。
您创建一个代表某种资源的类。当类被实例化(通过其构造函数之一)时,它应该获取资源,如果失败则抛出异常。当类被销毁时,它应该处理资源,执行所有必要的清理。
而且……就是这样。您还必须处理复制构造函数和赋值运算符(通过克隆资源或将这两个函数设为私有,这样它们就不会被调用)。
您不需要找到“一个好的实现”,因为您将自己编写几十个不同的实现。它们写起来很简单,而且它们不容易被重用,因为每个都包装了不同类型的资源。
有一个建议将 scope_guard 添加到标准库中。您可以在此处阅读该论文,其中包括一个您可以复制/粘贴的示例实现。实施见第 9.1 节。
MNMLSTC 核心具有作用域保护习语的现代 C++11 实现。