在我的代码中,我使用HANDLE
中的windows.h
。它们像
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<someType>
的内容,它解决了使用new
分配的资源问题。
但是,由于我不使用new
,因为HANDLE
只是typedef
编辑为void *
,我想知道我应该如何使用std::auto_ptr<someType>
}}。不知何故,应该可以给它一个自定义删除函数(if (!CloseHandle(h)) { throw std::exception("closeHandle error"); }
)。创建一个类将是另一种方法,因为析构函数在其实例超出范围时被调用。然而,为每一件简单的事情设一个课程真是太过分了。
如何修复这些意外内存泄漏?
请注意,我更喜欢纯C ++中没有库和大依赖关系的解决方案,除非它们非常小并且无论如何都要在大多数环境中使用。
答案 0 :(得分:6)
我想到的一个想法是将boost::shared_ptr与custom deleter一起使用。
答案 1 :(得分:3)
您可以实现自己的简单RAII习惯用法。
class auto_handle {
public:
auto_handle() : handle_() {}
~auto_handle() {
if (!CloseHandle(handle_)) {
// Don't throw here (1), manage the error in other way.
}
}
HANDLE& handle() { return handle_; }
private:
auto_handle(const auto_handle&);
auto_handle& operator=(const auto_handle&);
HANDLE handle_;
};
(1)You should never throw from a destructor。
auto_handle h;
if (!openHandleToSomething(arg1, arg2, &h.handle())) {
throw exception("openHandleToSomething error"); // Now it is safe
}
答案 2 :(得分:2)
1)不要使用auto_ptr<>
。认真。你不想要那些令人头疼的问题 - 它很容易滑倒b / c它没有熟悉的复制语义。
2)用一个简单的对象包装HANDLE
,该对象提供一个访问器,为您提供底层句柄。您需要将HANDLE
传递给API调用。 (我认为访问器比隐式转换更可取。)
3)我从来没有真正打扰包裹HANDLE
,所以我不知道是否有任何令人惊讶的问题。如果有,我不能指出它们。我不指望任何 - 这是一个不透明的价值。但那时,谁预计会出现令人惊讶的问题?毕竟,他们是惊喜。
4)(当然)实施适当的dtor。
答案 3 :(得分:1)
std::auto_ptr
不适合这种情况。它有它的用途,但这不是其中之一。为了纠正(排序)Greg D提出的一个问题,auto_ptr
的问题不在于它缺少指针语义,因为它相当奇怪的所有权语义 - 当你指定一个时,你没有获得指针的副本,而是指针的传输(即受让人成为新的唯一所有者资源,分配者不再有任何东西)。
您确实希望将句柄包装在一个类中。我已经做过很多次了,而且效果很好。在做这件事的时候我没有遇到任何特别令人惊讶的事情,虽然这并不一定意味着很多 - Windows中的很多东西都使用了句柄,其中一些可能很容易产生一些奇怪之处。
答案 4 :(得分:1)
你只需要一个简单的包装器,在你将它传递给函数时为你提供句柄:
#include <stdexcept>
class HWrapper
{
HANDLE h;
bool closed;
public:
HWrapper(A1 arg1,A2 arg2)
:closed(false)
{
if (!openHandleToSomething(arg1, arg2, &h))
{ throw std::runtime_error("openHandleToSomething error");
}
}
~HWrapper()
{
try
{
if (!closed)
{ close();
}
}
catch(...) {/*Exceptions should never leave a destructor */ }
// Though you may want to log somthing.
}
void close()
{
closed = true;
// Close can throw an exception.
if (!CloseHandle(h))
{ throw std::runtime_error("closeHandle error");
}
}
/*
* This allows you to just pass it to a function that takes an HANDLE
* See the function: functionThatUsesHandleButMayThrow();
*/
operator HANDLE()
{
return h;
}
private:
/*
* For your use case there is not need to copy.
* So explicitly disallow copying.
*
* Just pass the HWrapper object to any function that requires a handle.
* The built in cast operator will convert it back to a Handle to be used
* within these functions. While this object just retains ownership and
* responcability for deleting the object when you are finished.
*
* This allows simple backwards compatibility with existing code.
*/
HWrapper(HWrapper const& copy); // Don't implement
HWrapper& operator=(HWrapper const& copy); // Don't implement
};
void functionThatUsesHandleButMayThrow(HANDLE h)
{
}
int main()
{
try
{
HWrapper w(A1,A2);
functionThatUsesHandleButMayThrow(w);
/*
* If you do not care about close throwing an excepion.
* Then jsut let it fall out of scope. The destructor
* will try and clean up. But if it fails it will drop the
* exception.
*
* This is required because if another exception is propogating
* throwing an exception terminates the application.
*/
}
catch(std::exception const& e)
{
std::cout << "Exception: " << e.what() << "\n";
}
try
{
HWrapper w2(A1,A2);
functionThatUsesHandleButMayThrow(w2);
/*
* If you do care abou the exception
* The call close() manually. The exception will be thrown.
*
* But if an exception is already been thrown in
* functionThatUsesHandleButMayThrow() then we will try and close it
* in the destructor and not throw another exception.
*/
w2.close();
}
catch(std::exception const& e)
{
std::cout << "Exception: " << e.what() << "\n";
}
}
答案 5 :(得分:0)
HANDLE h;
if (!openHandleToSomething(arg1, arg2, &h)) {
throw std::exception("openHandleToSomething error");
}
这是:
auto d = [](decltype(h)* a) { if(a) ::CloseHandle(*a); };
std::unique_ptr<decltype(h), decltype(d)> buf(&h, d);