¿Qué clase de contenedor en C ++ debo utilizar para la gestión de recursos automatizado?

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

  •  19-09-2019
  •  | 
  •  

Pregunta

Soy un aficionado C ++. Estoy escribiendo algo de código API de Win32 y hay asas y objetos extrañamente compositely asignados en abundancia. Así que me preguntaba - ¿hay alguna clase de contenedor que haría más fácil la gestión de recursos

Por ejemplo, cuando quiero cargar algunos datos que abrir un archivo con CreateFile() y obtener un HANDLE. Cuando he terminado con ella, debería llamar CloseHandle() en él. Sin embargo, para cualquier función de carga razonablemente complejo habrá de posibles puntos de salida de decenas, por no hablar de excepciones.

Así que sería genial si pudiera envolver el mango en una especie de clase de contenedor que llame automáticamente una vez que la ejecución CloseHandle() dejó el alcance. Incluso mejor -. Podría hacer alguna referencia a contar para que pueda pasar por allí, además de otras funciones, y sería liberar el recurso sólo cuando la última referencia la izquierda alcance

El concepto es simple - pero ¿hay algo así en la biblioteca estándar? Estoy usando Visual Studio 2008, por cierto, y yo no quiero para fijar un marco de 3 ª parte como Boost o algo así.

¿Fue útil?

Solución

Escribir el suyo propio. Es sólo unas pocas líneas de código. Es sólo una tarea tan sencilla que no es pena para proporcionar una versión reutilizable genérico.

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

private:
  HANDLE h;
};

Piense en lo que es una versión genérica tendría que hacer: Tendría que ser parametrizable para que pueda especificar cualquier par de funciones y cualquier número de argumentos para ellos. Sólo una instancia de un objeto tal es probable que tomar tantas líneas de código como la definición de la clase anterior.

Por supuesto, C ++ 0x podría inclinar la balanza un tanto con la adición de expresiones lambda. Dos expresiones lambda podrían pasar fácilmente a una clase de contenedor genérico, por lo que una vez que C ++ 0x apoya, le vemos podría ver una clase RAII tan genérica añadido que potencien o algo así.

Sin embargo, en este momento, es más fácil simplemente rodar su propia siempre que lo necesite.

En cuanto a la adición de recuento de referencias, te aconsejo en contra de ella. recuento de referencias es caro (repentinamente su mango tiene que ser asignado dinámicamente, y los contadores de referencia tienen que ser mantenidos en cada asignación), y muy difícil de hacerlo bien. Es un área justo lleno de sutiles condiciones de carrera en un entorno roscado.

Si lo necesita recuento de referencias, simplemente hacer algo como boost::shared_ptr<FileWrapper>:. Envolver sus clases RAII ad-hoc personalizados en un shared_ptr

Otros consejos

En esencia, fstream es un buen envoltorio C ++ para los identificadores de archivo. Es parte de la norma que significa que es portátil, bien probado, y extensible de una manera orientada a objetos. Para los recursos de archivo, es un gran concepto.

Sin embargo, fstream sólo funciona para archivos, no para las manijas genéricas, es decir, hilos, procesos, objetos de sincronización, archivos mapeados en memoria, etc.

Estas envolturas se llaman ATL.

Si el mango es un evento o similares, utilice clase Chandle .

Si el mango es un archivo, utilice uno CAtlFile deriva, que se envuelve como API CreateFile y ReadFile.

No tenemos otros envoltorios útiles en ATL, CAtlFileMapping<T> es un envoltorio RAII sobre la memoria asignada archivos, CPath envuelve API Shell32 relativos a la trayectoria de manipulación, y así sucesivamente.

ATL es gran biblioteca, pero las cosas de bajo nivel como los archivos, las cuerdas y las colecciones son aislados. Usted las puede utilizar en todas las aplicaciones Win32. Se cabecera única, no es necesario para enlazar con cualquier cosa, o distribuir archivos DLL adicionales como MFC o CRT, el código se compila en llamadas WinAPI y simplemente funciona.

Se separó de MFC en VS2003 o 2005, no recuerdo, es decir, Visual Studio 2008 sin duda los tiene. Hay una advertencia sin embargo, si está utilizando una versión freeware de VS, debe ser 2015 o más nuevo.

Aquí hay una sede fuera del código de EnsureCleanup 'de Windows a través de C / C ++: http://www.codeproject.com/KB/cpp/template2003.aspx

MFC tiene algunas primitivas adecuadas (ver CFile por ejemplo), pero no la biblioteca estándar.

Visual C ++ 2008 es compatible con TR1 a través del paquete de características, y TR1 incluye shared_ptr. Me gustaría utilizar esta -. Que es una muy poderosa clase puntero inteligente y pueden generalizarse para hacer el tipo de gestión de recursos está solicitando

TR1 es efectivamente una extensión del estándar. Creo que sigue siendo oficialmente "pre-estándar", pero efectivamente se puede considerar bloqueado.

No creo que hay algo en la librería estándar, y también duda de que compartía punteros (como en el impulso) se puede utilizar (ya que los esperaría puntero de manejar, no manejan).

No debería ser difícil escribir uno usted mismo, siguiendo las href="http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Scope_Guard" modismo (y haciendo uso de las plantillas / punteros de función, etc. si así lo desea).

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>;
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top