Quelle classe wrapper en C ++ dois-je utiliser pour la gestion des ressources automatisée?

StackOverflow https://stackoverflow.com/questions/2433240

  •  19-09-2019
  •  | 
  •  

Question

Je suis un amateur C ++. Je vous écris un code API Win32 et il y a des poignées et des objets attribués weirdly profiteur de manière composite. Alors je me demandais - est-il une classe wrapper qui rendrait plus facile la gestion des ressources

Par exemple, quand je veux charger des données ouvrir un fichier avec CreateFile() et obtenir un HANDLE. Quand je suis fait avec elle, je devrais appeler CloseHandle() là-dessus. Mais pour toute fonction de chargement assez complexe, il y aura des dizaines de points de sortie possibles, sans parler des exceptions.

Alors, ce serait bien si je pouvais envelopper la poignée dans une sorte de classe wrapper qui appelle automatiquement une fois l'exécution CloseHandle() a quitté le champ. Mieux encore -. Il pourrait faire une référence compter que je peux passer autour dans et hors d'autres fonctions, et il libérerait la ressource que lorsque la dernière référence portée gauche

Le concept est simple - mais est-il quelque chose comme ça dans la bibliothèque standard? J'utilise Visual Studio 2008, en passant, et je ne veux pas fixer un cadre 3ème partie comme Boost ou quelque chose.

Était-ce utile?

La solution

Écrivez votre propre. Il est seulement quelques lignes de code. Il est juste une tâche simple qui n'est pas vaut pour fournir une version générique réutilisable.

struct FileWrapper {
  FileWrapper(...) : h(CreateFile(...)) {}
  ~FileWrapper() { CloseHandle(h); }

private:
  HANDLE h;
};

Pensez à ce qu'une version générique aurait à faire: Il faudrait être pour que vous puissiez paramétrables spécifier une paire de fonctions et un nombre des arguments leur. Juste instancier un tel objet serait probablement prendre autant de lignes de code que la définition de classe ci-dessus.

Bien sûr, C ++ 0x pourrait faire pencher la balance un peu avec l'ajout des expressions lambda. Deux expressions lambda pourraient facilement être transmis à une classe d'emballage générique, donc une fois que C ++ 0x soutient arrive, nous peut voir une telle classe RAII générique ajouté pour stimuler ou quelque chose.

Mais pour le moment, il est plus facile de rouler votre propre à chaque fois que vous en avez besoin.

En ce qui concerne l'ajout de comptage de référence, je vous le déconseille. Le compte de référence est cher (tout à coup votre poignée doit être réparti de façon dynamique, et les compteurs de référence doivent être maintenus sur chaque mission), et très difficile à obtenir droite. C'est une zone juste débordant de conditions de course subtiles dans un environnement fileté.

Si vous faire besoin de comptage de référence, il suffit de faire quelque chose comme boost::shared_ptr<FileWrapper>:. Enveloppez vos classes ad hoc personnalisés RAII dans un shared_ptr

Autres conseils

Pour l'essentiel, fstream est un bon C ++ wrapper pour les descripteurs de fichiers. Cela fait partie de la norme qui signifie qu'il est portable, bien testé et extensible de manière orientée objet. Pour les ressources de fichiers, il est un grand concept.

Cependant, fstream ne fonctionne que pour les fichiers, et non pour les poignées génériques, à savoir des fils, des procédés, des objets de synchronisation, les fichiers de configuration de mémoire, etc.

MFC a des CFile par exemple), mais pas la bibliothèque standard.

Visual C ++ 2008 prend en charge TR1 par le Feature Pack et TR1 comprend shared_ptr. J'utiliser -. Il est une classe de pointeur intelligent très puissant et peut être généralisé à faire le type de gestion des ressources que vous demandez

TR1 est en fait une extension de la norme. Je crois qu'il est encore officiellement « pré-standard », mais efficace vous pouvez le considérer verrouillé.

Je ne pense pas qu'il y ait quoi que ce soit dans la bibliothèque standard, et je doute aussi que les pointeurs partagés (comme en boost) peut être utilisé (puisque ceux pointeur s'attendrait à la poignée, pas MANCHE).

Il ne devrait pas être difficile d'écrire vous-même, en suivant les idiome (et utiliser des modèles / pointeurs de fonction, etc si vous le souhaitez).

template <typename Traits>
class unique_handle
{
    using pointer = typename Traits::pointer;

    pointer m_value;

    auto close() throw() -> void
    {
        if (*this)
        {
            Traits::close(m_value);
        }
    }

public:

    unique_handle(unique_handle const &) = delete;
    auto operator=(unique_handle const &)->unique_handle & = delete;

    explicit unique_handle(pointer value = Traits::invalid()) throw() :
        m_value{ value }
    {
    }

    unique_handle(unique_handle && other) throw() :
        m_value{ other.release() }
    {
    }

    auto operator=(unique_handle && other) throw() -> unique_handle &
    {
        if (this != &other)
        {
            reset(other.release());
        }

        return *this;
    }

    ~unique_handle() throw()
    {
        close();
    }

    explicit operator bool() const throw()
    {
        return m_value != Traits::invalid();
    }

    auto get() const throw() -> pointer
    {
        return m_value;
    }

    auto get_address_of() throw() -> pointer *
    {
        ASSERT(!*this);
        return &m_value;
    }

    auto release() throw() -> pointer
    {
        auto value = m_value;
        m_value = Traits::invalid();
        return value;
    }

    auto reset(pointer value = Traits::invalid()) throw() -> bool
    {
        if (m_value != value)
        {
            close();
            m_value = value;
        }

        return static_cast<bool>(*this);
    }

    auto swap(unique_handle<Traits> & other) throw() -> void
    {
        std::swap(m_value, other.m_value);
    }
};

template <typename Traits>
auto swap(unique_handle<Traits> & left,
    unique_handle<Traits> & right) throw() -> void
{
    left.swap(right);
}

template <typename Traits>
auto operator==(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() == right.get();
}

template <typename Traits>
auto operator!=(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() != right.get();
}

template <typename Traits>
auto operator<(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() < right.get();
}

template <typename Traits>
auto operator>=(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() >= right.get();
}

template <typename Traits>
auto operator>(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() > right.get();
}

template <typename Traits>
auto operator<=(unique_handle<Traits> const & left,
    unique_handle<Traits> const & right) throw() -> bool
{
    return left.get() <= right.get();
}

struct null_handle_traits
{
    using pointer = HANDLE;

    static auto invalid() throw() -> pointer
    {
        return nullptr;
    }

    static auto close(pointer value) throw() -> void
    {
        VERIFY(CloseHandle(value));
    }
};

struct invalid_handle_traits
{
    using pointer = HANDLE;

    static auto invalid() throw() -> pointer
    {
        return INVALID_HANDLE_VALUE;
    }

    static auto close(pointer value) throw() -> void
    {
        VERIFY(CloseHandle(value));
    }
};

using null_handle = unique_handle<null_handle_traits>;
using invalid_handle = unique_handle<invalid_handle_traits>;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top