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

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

  •  19-09-2019
  •  | 
  •  

Вопрос

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

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

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

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

Это было полезно?

Решение

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

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

private:
  HANDLE h;
};

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

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

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

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

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

Другие советы

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

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

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

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

Если ваш дескриптор является файлом, используйте производный от 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 года выпуска или новее.

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

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

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

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

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

Написать его самостоятельно не должно составить труда, следуя защита прицела идиома (и использование шаблонов / указателей на функции и т.д., если вы того пожелаете).

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>;
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top