使非对象资源符合 RAII 标准
在我的代码中,我使用 windows.h
中的 HANDLE
。它们的使用方式
HANDLE h;
if (!openHandleToSomething(arg1, arg2, &h)) {
throw std::exception("openHandleToSomething error");
}
/* Use the handle in other functions which can throw as well */
if (!CloseHandle(h)) {
throw std::exception("closeHandle error");
}
如您所见,您必须将此 CloseHandle
插入到获取和释放过程中可能发生的每个异常中。因此,您很可能忘记了一个(或者有一个您不知道的奇特的 SEH 异常),然后瞧,您遇到了内存泄漏。
最近,我读到了有关 RAII 的内容,它应该可以消除此类情况下的麻烦,并且应该自动调用此 CloseHandle
。我还看到 C++ 中有类似 std::auto_ptr
的东西,它解决了用 new
分配的资源的问题。
但是,由于我不使用 new
并且 HANDLE
只是 typedef
编辑为 void *
,我想知道应该如何使用 std::auto_ptr
。不知何故,应该可以给它一个自定义删除器函数(if (!CloseHandle(h)) { throw std::exception("closeHandle error"); }
)。创建一个类将是另一种方法,因为只要它的实例超出范围,就会调用析构函数。然而,为每件简单的事情都建立一个类就太过分了。
如何修复这些意外的内存泄漏?
请注意,我更喜欢纯 C++ 的解决方案,没有库和大的依赖项,除非它们确实很小并且在大多数环境中使用。
in my code I use HANDLE
s from windows.h
. They are used like
HANDLE h;
if (!openHandleToSomething(arg1, arg2, &h)) {
throw std::exception("openHandleToSomething error");
}
/* Use the handle in other functions which can throw as well */
if (!CloseHandle(h)) {
throw std::exception("closeHandle error");
}
As you see, you have to insert this CloseHandle
to every exception which can happen in the middle of acquiration and release. Therefore, it's likely you forget one (or there is a fancy SEH exception which you didn't know about) and voilà, you have your memory leak.
Recently, I've read about RAII which should remove the headaches from such cases and should call this CloseHandle
automatically. I've also seen that there is something like std::auto_ptr<someType>
in C++ which solves the problem for resources which were allocated with new
.
However, since I don't use new
and since HANDLE
is just typedef
ed to be a void *
, I wonder how I should use the std::auto_ptr<someType>
. Somehow, it should be possible to give it a custom deleter function (if (!CloseHandle(h)) { throw std::exception("closeHandle error"); }
). Creating a class would be another method since the destructor gets called any time an instance of it gets out of scope. However, it's just overkill to have a class for every simple thing.
How can I fix these accidential memory leaks?
Note that I would prefer solutions which are in pure C++ with no libraries and big dependencies except if they are really small and used in most of the environments anyways.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我想到的一个想法是使用 boost::shared_ptr< /a> 带有自定义删除器。
One idea that comes to mind is to use boost::shared_ptr with a custom deleter.
您可以实现自己的简单 RAII 习惯用法。
(1) 你不应该从析构函数中抛出 。
You can implement your own simple RAII idiom.
(1) You should never throw from a destructor.
1) 不要使用
auto_ptr
。严重地。你不想要那些令人头疼的事情——因为它没有熟悉的复制语义,所以很容易出错。2) 用一个简单的对象包装
HANDLE
,该对象提供一个为您提供底层句柄的访问器。您需要将HANDLE
传递给 API 调用。 (我认为访问器比隐式转换更可取。)3)我从来没有真正费心包装
HANDLE
,所以我不知道是否有任何令人惊讶的陷阱。如果有的话,我也无法指出。我不期望任何——这是一个不透明的值。但是,谁会想到一个令人惊讶的陷阱呢?毕竟,它们是惊喜。4)(当然)实施适当的dtor。
1) Don't use
auto_ptr<>
. Seriously. You don't want those headaches-- it's far too easy to slip up b/c it doesn't have familiar copy semantics.2) Wrap
HANDLE
with a simple object that provides an accessor that gives you the underlying handle. You'll need this to pass theHANDLE
in to API calls. (I'd consider an accessor preferable to an implicit conversion.)3) I've never actually bothered wrapping
HANDLE
, so I don't know if there are any surprising gotcha's. If there are, I can't point them out. I wouldn't expect any-- it's an opaque value. But then, who expects a surprising gotcha'? They're surprises, after all.4) (Of course) implement the appropriate dtor.
std::auto_ptr
不适合这种情况。它有其用途,但这不是其中之一。为了纠正(某种程度上)Greg D 提出的观点,auto_ptr
的问题并不在于缺乏指针语义,而是在于其相当奇怪的所有权 语义——当你分配一个指针时,你不会得到指针的副本,而是指针的传输(即受让人成为资源的新唯一所有者,分配者不再拥有任何东西)。不过,您确实想将句柄包装在一个类中。我已经这样做过很多次了,而且效果非常好。在执行此操作时,我没有遇到任何特别令人惊讶的事情,尽管这并不一定意味着很多——句柄在 Windows 中用于很多事情,其中一些可能很容易出现一些奇怪的情况。
std::auto_ptr
is not suitable for this situation. It has its uses, but this isn't one of them. To correct (sort of) a point raised by Greg D, the problem withauto_ptr
isn't so much its lack of pointer semantics, as its rather odd ownership semantics -- when you assign one, you don't get a copy of the pointer, but instead a transfer of the pointer (i.e the assignee becomes the new sole owner of the resource, and the assigner no longer has anything).You do want to wrap the handle in a class though. I've done this a number of times, and it works quite well. I haven't run into anything particularly surprising when doing it, though that doesn't necessarily mean a lot -- handles are used for a lot of things in Windows, and some of them might easily have some oddities.
您只需要一个简单的包装器,当您将句柄传递给函数时,它可以为您提供句柄:
You just want a simple wrapper that gives you the handle when you pass it into a function:
这里是 :
Here it is :