Как перемещать классы, содержащие HANDLE из WinAPI? C++
Согласно документации HANDLE - это просто указатель типа void*. Я решил сделать обертку-класс для этого указателя, чтобы контролировать его цикл жизни. Выглядит примерно так:
class AnyHandler {
private:
HANDLE handle;
public:
AnyHandler();
AnyHandler(const AnyHandler&);
AnyHandler(AnyHandler&&);
~AnyHandler();
AnyHandler& operator=(const AnyHandler&);
AnyHandler& operator=(AnyHandler&&);
AnyHandler(const HANDLE);
AnyHandler& operator=(const HANDLE);
const HANDLE get() const;
void set(const HANDLE);
};
Вопрос таков: как реализовывать конструкторы копирования и перемещения для HANDLE? Ведь если у нас будет конструктор копирования + конструктор перемещения, то копирование должно быть глубоким. То есть с выделением новой динамической памяти и копирование в него объекта, на который указывает исходный HANDLE. Но мы не знаем ни тип, ни на что указывает void*. В таком случае напрашивается вывод, что перемещение для данной обертки не имеет смысла и можно оставить просто поверхностное копирование указателя в конструкторе копирования? Но ведь в таком случае у нас может быть два объекта, указывающих на один и тот же адрес памяти. И будет плохо, если один из них удалится, освободив память, на которую указывает HANDLE. В таком случае второй указатель станет висячим и вызовет проблемы при попытке разыменовать его или освободить. Как поступить в таком случае?
Ответы (1 шт):
Все такие обертки пишутся одинаково. Я бы делал ее не универсальной, а под конкретный тип содержимого.
Даже если реализовать копирование проблематично, я не вижу что мешает сделать хотя бы перемещение.
class Foo
{
// Если больше одного поля, сложите их во вложенную структуру, для удобства
// написания перемещающего конструктора и оператора присваивания.
HANDLE handle = nullptr;
public:
Foo() {}
// Конструктор, который заполняет чем-то `handle`.
Foo(...) {...}
Foo(Foo &&other) noexcept : handle(std::exchange(other.handle, nullptr)) {}
// Заметьте, нет `&&`.
// Прием назвается copy&swap idiom.
Foo &operator=(Foo other) noexcept
{
std::swap(handle, other.handle);
return *this;
}
~Foo()
{
if (handle)
// уничтожить handle
}
};
Если хотите, добавьте конструктор копирования. При этом копирующий operator= не нужен, потому что этот может работать и как перемещающий (если есть перемещающий конструктор), и как копирующий (если есть копирующий конструктор).
Еще, в таких обертках хорошо смотрится explicit operator bool() const {return bool(handle);}, чтобы проверять их на пустоту.