我碰到了多年前由Andrei Alexandrescu和Petru Marginean撰写的这篇文章,其中提出并讨论了一个名为ScopeGuard的实用程序类,用于编写异常安全代码。 我想知道使用这些对象进行编码是否确实可以带来更好的代码,或者它混淆了错误处理,因此,在catch块中更好地呈现警卫队的回调? 有没有人在实际生产代码中使用这些工具有经验?
它肯定会改善您的代码。您初步提出的说法是,它晦涩难懂,并且代码应从catch块中获取,这在C ++中根本不成立,因为RAII是既定的习惯用法。 C ++中的资源处理是通过资源获取来完成的,而垃圾回收是通过隐式析构函数的调用来完成的。
另一方面,显式的catch块会使代码膨胀,并引入细微的错误,因为代码流变得更加复杂,并且必须显式地进行资源处理。
RAII(包括ScopeGuard)在C ++中并不是一种晦涩的技术,但已确立了最佳实践。
是。
如果只有一条C ++代码可以建议每个C ++程序员花10分钟学习,那就是ScopeGuard(现在是可免费使用的Loki库的一部分)。
我决定尝试将ScopeGuard(稍作修改)版本用于我正在开发的小型Win32 GUI程序。您可能知道Win32具有许多不同类型的资源,需要以不同的方式关闭(例如,内核句柄通常使用CloseHandle()关闭,GDI BeginPaint()需要与EndPaint()配对等),我使用了ScopeGuard所有这些资源,还用于分配带有new的工作缓冲区(例如,用于与Unicode的字符集转换)。
令我惊讶的是程序缩短了多少。基本上,这是双赢的:您的代码同时变得更短,更健壮。将来的代码更改不会泄漏任何内容。他们只是做不到。多么酷啊?
我经常使用它来保护内存使用,这些是需要释放的东西,它们是从OS返回的。例如:
1 2 3 4 5 6 7
| DATA_BLOB blobIn, blobOut;
blobIn.pbData=const_cast<BYTE*>(data);
blobIn.cbData=length;
CryptUnprotectData(&blobIn, NULL, NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &blobOut);
Guard guardBlob=guardFn(::LocalFree, blobOut.pbData);
// do stuff with blobOut.pbData |
是。
在C ++中是如此重要,以至于D中甚至是一种特殊的语法:
1 2 3 4 5 6 7
| void somefunction() {
writeln("function enter");
// c++ has similar constructs but not in syntax level
scope(exit) writeln("function exit");
// do what ever you do, you never miss the function exit output
} |
我认为以上答案缺少一个重要说明。正如其他人指出的那样,可以使用ScopeGuard来释放分配的资源,而不受故障(异常)的影响。但这可能不是您要使用示波器保护的唯一内容。实际上,链接文章中的示例将ScopeGuard用于不同的目的:交易。简而言之,如果您有多个对象(即使这些对象正确使用了RAII),则需要保持某种状态的关联,这可能会很有用。如果这些对象中任何一个的状态改变导致异常(我想,这通常意味着其状态没有改变),那么所有已经应用的改变都需要回滚。这就产生了它自己的一系列问题(如果回滚也失败怎么办?)。您可以尝试推出自己的类来管理此类相关对象,但是随着此类数量的增加,它会变得凌乱,并且无论如何您都可能会回到内部使用ScopeGuard。
我没有使用过这个特定的模板,但是我之前使用过类似的东西。是的,与以不同方式实现的同样健壮的代码相比,它的确可以使代码更清晰。
我必须说,不,不,不是。这里的答案有助于说明为什么这是一个真正可怕的想法。资源处理应通过可重用的类完成。他们使用范围保护器实现的唯一一件事就是违反wazoo的要求,并在整个代码库中复制其资源释放代码,而不是编写一个类来处理整个资源。
如果范围警卫有任何实际用途,则资源处理不是其中之一。在这种情况下,它们比普通的RAII逊色得多,因为RAII已进行重复数据删除,并且自动保护和范围保护是手动代码复制或无效。