Какой класс-оболочку в C ++ я должен использовать для автоматического управления ресурсами? - PullRequest
15 голосов
/ 12 марта 2010

Я любитель C ++. Я пишу некоторый код Win32 API, и есть множество дескрипторов и странно составных объектов. Поэтому мне было интересно - есть ли какой-нибудь класс-оболочка, который бы облегчил управление ресурсами?

Например, когда я хочу загрузить некоторые данные, я открываю файл с CreateFile() и получаю HANDLE. Когда я закончу, я должен позвонить CloseHandle(). Но для любой достаточно сложной функции загрузки будут десятки возможных точек выхода, не говоря уже об исключениях.

Так что было бы здорово, если бы я мог обернуть дескриптор в какой-нибудь класс-обертку, который автоматически вызовет CloseHandle(), как только выполнение выйдет из области действия. Еще лучше - он мог бы выполнять подсчет ссылок, чтобы я мог передавать его в другие функции и выходить из него, и он освободил бы ресурс только тогда, когда последняя ссылка вышла из области действия.

Концепция проста - но есть ли что-то подобное в стандартной библиотеке? Кстати, я использую Visual Studio 2008 и не хочу подключать сторонние фреймворки, такие как Boost или что-то в этом роде.

Ответы [ 8 ]

11 голосов
/ 12 марта 2010

Напишите свой собственный. Это всего лишь несколько строк кода. Это просто такая простая задача, что не стоит предоставления универсальной многоразовой версии.

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

private:
  HANDLE h;
};

Подумайте о том, что должна делать универсальная версия: она должна быть параметризованной, чтобы вы могли указать любую пару функций и любое количество аргументов для них. Простое создание экземпляра такого объекта может занять столько строк кода, сколько указано в приведенном выше определении класса.

Конечно, C ++ 0x может несколько изменить баланс с добавлением лямбда-выражений. Два лямбда-выражения можно легко передать в универсальный класс-оболочку, поэтому, как только появится поддержка C ++ 0x, мы могли бы увидеть, что такой универсальный класс RAII добавлен в Boost или что-то в этом роде.

Но на данный момент проще бросить свой собственный, когда вам это нужно.

Что касается добавления подсчета ссылок, я бы посоветовал против этого. Подсчет ссылок стоит дорого (внезапно ваш дескриптор должен быть выделен динамически, а счетчики ссылок должны поддерживаться при каждом назначении), и его очень сложно получить правильно. Это область, изобилующая тонкими условиями гонки в многопоточной среде.

Если вам нужно подсчитывать ссылки, просто сделайте что-то вроде boost::shared_ptr<FileWrapper>: оберните ваши специальные классы RAII в shared_ptr.

2 голосов
/ 28 июня 2019

Эти оболочки называются ATL.

Если ваш дескриптор является событием или подобным, используйте CHandle class.

Если ваш дескриптор является файлом, используйте производный от CAtlFile файл, он оборачивает такие API-интерфейсы, как CreateFile и ReadFile.

В ATL есть и другие полезные оболочки, CAtlFileMapping<T> - это оболочка RAII для файлов, отображаемых в память, CPath - обертки API-интерфейсов shell32 для обработки пути и т. Д.

ATL - это большая библиотека, но вещи низкого уровня, такие как файлы, строки и коллекции, изолированы. Вы можете использовать их во всех приложениях Win32. это только заголовок, вам не нужно ни с чем связываться, или распространять дополнительные DLL, такие как MFC или CRT, код компилируется в вызовы WinAPI и просто работает.

Они были отделены от MFC в VS2003 или 2005, не помню, т. Е. Visual Studio 2008 определенно их имеет. Однако есть одна оговорка: если вы используете бесплатную версию VS, это должна быть версия 2015 года или новее.

2 голосов
/ 12 марта 2010

По сути, fstream - хорошая оболочка C ++ для файловых дескрипторов. Это часть стандарта, которая означает, что она переносима, хорошо протестирована и расширяема объектно-ориентированным способом. Для файловых ресурсов это отличная концепция.

Однако fstream работает только для файлов, но не для общих дескрипторов, т. Е. Потоков, процессов, объектов синхронизации, отображенных в память файлов и т. Д.

1 голос
/ 12 марта 2010

Вот пример, основанный на коде EnsureCleanup из Windows через C / C ++: http://www.codeproject.com/KB/cpp/template2003.aspx

0 голосов
/ 10 мая 2016
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>;
0 голосов
/ 12 марта 2010

Я не думаю, что в стандартной библиотеке есть что-либо, и я также сомневаюсь, что могут использоваться общие указатели (как в boost) (поскольку они ожидают указатель на HANDLE, а не HANDLE).

Это не должно быть сложно написать самостоятельно, следуя идиоме scope guard (и используя шаблоны / указатели функций и т. Д., Если хотите).

0 голосов
/ 12 марта 2010

Visual C ++ 2008 поддерживает TR1 через Feature Pack, а TR1 включает shared_ptr. Я бы использовал это - это очень мощный класс интеллектуальных указателей, и его можно обобщить для выполнения требуемого вами типа управления ресурсами.

TR1 фактически является расширением к Стандарту. Я полагаю, что это все еще официально «предстандарт», но вы можете считать его заблокированным.

0 голосов
/ 12 марта 2010

MFC имеет несколько подходящих примитивов (например, CFile ), но не стандартную библиотеку.

...