【发布时间】:2020-06-06 09:23:52
【问题描述】:
我正在尝试编写一个 DriverIterator 类来迭代我通勤中的所有卷。
我知道下一个类可能会导致内存泄漏,因为:
current_ = std::make_unique<Driver>(paths);
可能会抛出异常(出于某种原因..),因此构造将不会完成,因此不会调用析构函数并且不会正确关闭句柄。
据我所知,一旦我收到句柄,我应该停止构建。 但我怎样才能做到这一点? FindFirstVolumeW 还提供了我需要在构造函数完成之前使用的数据。
DriverIterator.hpp:
class DriverIterator final
{
public:
explicit DriverIterator();
~DriverIterator();
private:
std::unique_ptr<Driver> current_;
bool is_empty_;
HANDLE handle_;
public:
bool is_empty() const;
Driver get_current() const;
void next();
private:
HANDLE start_find();
public:
DriverIterator(const DriverIterator&) = delete;
DriverIterator(DriverIterator&&) = delete;
DriverIterator& operator=(const DriverIterator&) = delete;
DriverIterator& operator=(DriverIterator&&) = delete;
};
DriverIterator.cpp:
DriverIterator::DriverIterator():
handle_(start_find())
{}
DriverIterator::~DriverIterator()
{
try
{
FindVolumeClose(handle_);
}
catch (...)
{
}
}
HANDLE DriverIterator::start_find()
{
static constexpr uint32_t MAX_BUFFER_SIZE = 1024;
wchar_t buffer[MAX_BUFFER_SIZE];
HANDLE handle = FindFirstVolumeW(buffer, MAX_BUFFER_SIZE);
if (handle == INVALID_HANDLE_VALUE)
{
//throw exception
}
wchar_t paths[1024];
DWORD res_size;
if (!GetVolumePathNamesForVolumeNameW(buffer, paths, 1024, &res_size))
{
//throw exception
}
current_ = std::make_unique<Driver>(paths);
is_empty_ = false;
return handle;
}
bool DriverIterator::is_empty() const
{
return is_empty_;
}
Driver DriverIterator::get_current() const
{
if (is_empty_)
{
//throw exception
}
return *current_;
}
void DriverIterator::next()
{
//code...
}
【问题讨论】:
-
你确定 shared_ptr 是正确的方式吗?由于您不允许复制和分配,因此该指针永远不会共享。我怀疑 unique_ptr 更适合这里
-
至于您的担心,如果 Driver 类编写正确,其析构函数中的异常不应导致任何不良影响或资源泄漏。由于 Driver 是通过智能指针 make_shared 或 make_unique 构建和维护的,因此这里没有潜在的泄漏,除非 Driver 或 DriverIterator 的用户存在泄漏
-
@MichaelVeksler 你是对的,我应该使用唯一指针而不是共享指针。但是,当我谈到内存泄漏时,我是在谈论无法正确关闭的句柄(从 FindFirstVolumeW 接收),因为 DriverIterator 的构造函数可能会在通过唯一指针构造 Driver 对象时抛出异常。
-
使用自定义删除器将您的临时资源绑定到
unique_ptr。 -
正如@IInspectable 提到的,您可以让带有自定义删除器的 unique_ptr 管理句柄。但就目前而言,我认为您没有任何问题。 Driver对象没有在构造函数中分配,所以构造函数中不会出现异常。但是,最安全的做法是使用 RAII 对象存储任何资源(s.t. 句柄)并在任何相关情况下释放它们。
标签: c++ winapi constructor memory-leaks raii