Was Wrapper-Klasse in C ++ soll ich für die automatisierte Ressourcenmanagement verwenden?

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

  •  19-09-2019
  •  | 
  •  

Frage

Ich bin ein C ++ Amateur. Ich schreibe einige Win32-API-Code und gibt es Griffe und weirdly compositely in Hülle und Fülle zugeordneten Objekte. Also ich frage mich - ist es eine Wrapper-Klasse, das einfache Ressourcenmanagement machen würde

Zum Beispiel, wenn ich ein paar Daten zu ladende öffne ich eine Datei mit CreateFile() und HANDLE bekommen. Wenn ich mit ihm fertig bin, sollte ich CloseHandle() auf es nennen. Aber für jede einigermaßen komplexe Ladefunktion gibt es Dutzende von möglichen Austrittsstellen sein, keine Ausnahmen zu nennen.

So wäre es toll, wenn ich den Griff in einer Art Wrapper-Klasse wickeln könnte, die automatisch CloseHandle() nennen würden, sobald die Ausführung den Umfang verlassen. Noch besser -. Es könnte eine Referenzzählung tun, damit ich es um in und aus anderen Funktionen übergeben kann, und es würde lassen Sie die Ressource nur dann, wenn der letzte Verweis Umfang links

Das Konzept ist einfach - aber gibt es so etwas wie die in der Standard-Bibliothek? Ich bin mit Visual Studio 2008, nebenbei bemerkt, und ich will nicht, eine 3rd-Party Framework wie Boost-oder etwas befestigen.

War es hilfreich?

Lösung

Schreiben Sie Ihre eigene. Es ist nur ein paar Zeilen Code. Es ist nur eine so einfache Aufgabe, dass es nicht wert eine generische wiederverwendbare Version zur Verfügung zu stellen.

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

private:
  HANDLE h;
};

Denken Sie darüber nach, was eine generische Version zu tun haben würde: Es wäre parametrisierbar sein müssen, so dass Sie alle Paar Funktionen angeben können, und jeder Anzahl von Argumenten Sie. Nur Instanziieren ein solches Objekt wahrscheinlich so viele Zeilen Code wie die obige Klassendefinition nehmen würde.

Natürlich, C ++ 0x könnte das Gleichgewicht etwas mit dem Zusatz von Lambda-Ausdrücke kippen. Zwei Lambda-Ausdrücke leicht zu einer generischen Wrapper-Klasse weitergegeben werden konnten, so dass, sobald C ++ 0x unterstützt herum kommt, wir könnte sehen eine solche generische RAII Klasse Boost oder etwas hinzugefügt.

Aber im Moment, es ist einfacher, nur Ihre eigene Rolle, wenn Sie es brauchen.

Wie für Referenzzählung hinzufügen, würde ich davon abraten. Referenzzählung ist teuer (plötzlich Ihr Griff dynamisch zugewiesen werden, und die Bezugszähler haben auf jeder Zuweisung beibehalten werden) und sehr schwer richtig zu machen. Es ist ein Gebiet nur mit dezenten Rennbedingungen in einer Gewinde Umgebung platzen.

Wenn Sie tun Notwendigkeit Referenzzählung, nur so etwas wie boost::shared_ptr<FileWrapper> tun. Wickeln Sie Ihre individuelle Ad-hoc-RAH-Klassen in einem shared_ptr

Andere Tipps

Im Wesentlichen ist fstream ein guter C ++ Wrapper für Datei-Handles. Es ist Teil der Norm, die es bedeutet, ist tragbar, gut getestet und erweiterbare in einer objektorientierten Weise. Für Dateiressourcen, ist es ein großartiges Konzept.

Allerdings fstream funktioniert nur für Dateien, nicht für generische Griffe, das heißt Threads, Prozesse, Synchronisationsobjekte, Memory-Mapped-Dateien, etc.

Diese Wrapper namens ATL.

Wenn Ihr Griff ist ein Ereignis oder ein ähnliches, verwendet CHandle Klasse.

Wenn der Griff eine Datei ist, verwenden Sie CAtlFile ein abgeleitet, es wickelt APIs wie Createfile und Readfile.

Es sind andere nützliche Wrapper in ATL, ist CAtlFileMapping<T> ein RAII Wrapper über Memory-Mapped-Dateien, CPath Wraps shell32 APIs für Pfad Handhabung, und so weiter.

ATL ist große Bibliothek, aber Low-Level Dinge wie Dateien, Strings und Sammlungen isoliert sind. Sie können sie in allen Win32-Anwendungen verwenden. Header nur, Sie müssen nicht mit irgendetwas zu Link oder verteilen zusätzliche DLLs wie MFC oder CRT, den Code kompiliert in WinAPI Anrufe und funktioniert einfach.

Sie wurden von MFC in VS2003 oder 2005 geteilt, kann mich nicht erinnern, das heißt Visual Studio 2008 auf jeden Fall hat sie. Es gibt eine Einschränkung jedoch, wenn Sie eine Freeware-Version von VS verwenden, müssen sie 2015 oder neuer sein.

Hier ist eine Basis von dem EnsureCleanup Code von 'Windows über C / C ++: http://www.codeproject.com/KB/cpp/template2003.aspx

MFC hat einige geeignete Primitiven (Blick auf CFile zum Beispiel), aber nicht die Standard-Bibliothek.

Visual C ++ 2008 unterstützt TR1 durch das Feature Pack und TR1 enthält shared_ptr. Ich würde dies nutzen -. Es ist eine sehr leistungsfähige Smart-Pointer-Klasse ist und kann verallgemeinert werden, um die Art des Ressourcenmanagements zu tun für Sie fragen

TR1 ist effektiv eine Erweiterung des Standard. Ich glaube, es ist immer noch offiziell als „Pre-Standard“, aber effektiv können Sie halten es gesperrt.

Ich glaube nicht, dass irgendetwas in der Standard-Bibliothek ist, und ich, dass auch Zweifel Zeiger geteilt (wie in Boost) verwendet werden kann (da diese Zeiger auf GREIFEN erwarten würde, nicht GREIFT).

Es sollte nicht schwer sein, selbst eine Beurteilung zu schreiben, nach dem Umfang guard Idiom (und die Nutzung von Vorlagen / Funktionszeiger usw., wenn Sie so wählen).

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>;
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top