Pergunta

Eu sou um amador C ++. Estou a escrever algum código API Win32 e existem alças e objetos estranhamente compositely alocados em grande quantidade. Então eu queria saber? - há alguma classe de invólucro que tornaria mais fácil a gestão dos recursos

Por exemplo, quando eu quero carregar alguns dados que eu abrir um arquivo com CreateFile() e obter um HANDLE. Quando eu sou feito com ele, eu deveria chamar CloseHandle() nele. Mas para qualquer função de carregamento razoavelmente complexo, haverá dezenas de possíveis pontos de saída, para não mencionar exceções.

Portanto, seria ótimo se eu pudesse envolver a alça em algum tipo de classe de mensagens publicitárias que chamaria automaticamente CloseHandle() uma vez execução deixou o escopo. Ainda melhor -. Ele poderia fazer alguma contagem de referência para que eu possa passá-lo em torno de dentro e fora de outras funções, e que iria liberar o recurso somente quando a última referência deixou escopo

O conceito é simples - mas existe algo parecido na biblioteca padrão? Estou usando o Visual Studio 2008, a propósito, e eu não deseja anexar um quadro como o 3o partido impulso ou algo assim.

Foi útil?

Solução

Escreva a sua própria. É apenas algumas linhas de código. É apenas uma tarefa tão simples que não é vale a pena para fornecer uma versão reutilizável genérico.

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

private:
  HANDLE h;
};

Pense no que uma versão genérica teria que fazer: Ele teria que ser parametrizável para que você possa especificar qualquer par de funções, e qualquer número de argumentos para eles. Apenas instanciar tal objeto seria provavelmente ter tantas linhas de código como a definição de classe acima.

Claro, C ++ 0x pode pender a balança um pouco com a adição de expressões lambda. Duas expressões lambda pode ser facilmente passado para uma classe wrapper genérico, então quando C ++ 0x suportes vem por aí, nós pode ver um tal classe RAII genérico adicionados para impulsionar ou algo assim.

Mas, no momento, é mais fácil simplesmente rolar seus próprios sempre que você precisar.

Como para adicionar contagem de referência, eu aconselho contra ele. Contagem de referência é caro (de repente o seu identificador tem a ser alocada dinamicamente e contadores de referência tem que ser mantida em cada atribuição), e muito difícil de acertar. É uma área apenas rebentar com as condições de corrida sutis em um ambiente de rosca.

Se você do contagem de referência necessidade, basta fazer algo como boost::shared_ptr<FileWrapper>:. Envolver seu costume ad-hoc RAII classes em um shared_ptr

Outras dicas

Essencialmente, fstream é uma boa invólucro C ++ para identificadores de arquivo. É parte da norma o que significa que é portátil, bem testado, e extensível de forma orientada para o objecto. Para recursos de arquivo, é um grande conceito.

No entanto, fstream só funciona para arquivos, não para alças genéricos, ou seja threads, processos, objetos de sincronização, arquivos mapeados na memória, etc.

Estas embalagens são chamados ATL.

Se o seu punho é um evento ou similar, use CHandle classe.

Se o seu identificador é um arquivo, use CAtlFile derivada um, que envolve APIs como CreateFile e ReadFile.

Não são outros invólucros úteis em ATL, CAtlFileMapping<T> é um wrapper RAII sobre a memória arquivos mapeados, APIs CPath envoltórios Shell32 para manuseio caminho, e assim por diante.

ATL é grande biblioteca, mas as coisas de baixo nível, como arquivos, cordas e coleções são isolados. Você pode usá-los em todas as aplicações Win32. é somente cabeçalho, você não precisa de ligação com qualquer coisa, ou distribuir DLLs extras, como MFC ou CRT, os compila código para chamadas de WinAPI e simplesmente funciona.

Eles foram divididos de MFC no VS2003 ou 2005, não me lembro, ou seja, Visual Studio 2008 definitivamente tem. Há uma ressalva no entanto, se você estiver usando uma versão freeware do VS, deve ser 2015 ou mais recente.

Aqui está um baseado fora do código EnsureCleanup de 'Windows via C / C ++': http://www.codeproject.com/KB/cpp/template2003.aspx

MFC tem algumas primitivas adequadas (olhada CFile por exemplo), mas não a biblioteca padrão.

Visual C ++ 2008 suporta TR1 através do Feature Pack, e TR1 inclui shared_ptr. Gostaria de usar este. - É uma classe ponteiro inteligente muito poderoso e pode ser generalizada para fazer o tipo de gestão de recursos que você está pedindo

TR1 é efetivamente uma extensão do padrão. Eu acredito que ainda é oficialmente "pré-padrão", mas efetivamente você pode considerá-lo bloqueado.

Eu não acho que há alguma coisa na biblioteca padrão, e eu também dúvida de que os ponteiros (como em boost) compartilhada pode ser usado (desde aqueles esperaria ponteiro de manusear, não PUNHO).

Não deve ser difícil escrever um você mesmo, seguindo o guarda escopo idioma (e fazendo uso de templates / ponteiros de função, etc, se você preferir).

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top