Как перемещать классы, содержащие 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 шт):

Автор решения: HolyBlackCat

Все такие обертки пишутся одинаково. Я бы делал ее не универсальной, а под конкретный тип содержимого.

Даже если реализовать копирование проблематично, я не вижу что мешает сделать хотя бы перемещение.

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);}, чтобы проверять их на пустоту.

→ Ссылка