Подтвердить что ты не робот

Где правильная (обработка ресурсов) Правило нуля?

Вот статья, в которой говорится об идиоме под названием Rule of Zero.

Здесь выдержка:

class module {
public:
    explicit module(std::wstring const& name)
    : handle { ::LoadLibrary(name.c_str()), &::FreeLibrary } {}

    // other module related functions go here

private:
    using module_handle = std::unique_ptr<void, decltype(&::FreeLibrary)>;

    module_handle handle;
};

Он повторно использует функции unique_ptr RAII, поэтому вам не нужно заботиться о реализации сложной и многоплодной обертки из правила пяти.

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

  • Один из них способен сверять и использовать базовый тип, на который построен #define (или typedef) HANDLE. Для меня это должно быть скрытое знание, и решение должно основываться исключительно на том, что делает интерфейс: HANDLE.
  • Ручки могут быть любыми, они не должны быть типами указателей.

Я бы хотел использовать эту идиому, но во многих ситуациях я наткнулся на нее.

Является ли эта обертка, сфокусированная на RAII, уже сделанной и полезной в какой-нибудь классной библиотеке? Все ли используют такой инструмент, и я не знаю? (Я считаю, что неплохо иметь такую ​​оснастку не только для одного, но и для многих типов владельцев)

РЕДАКТИРОВАТЬ 1

Речь идет не о дескрипторах ресурсов платформы, например, glGenLists возвращает вид дескриптора, a GLuint, и вы должны называть glDeleteLists на нем. Как уже указывалось, ручками ресурсов не являются типы указателей, и это предположение не следует принимать.

EDIT 2

Правило Zero, в прежнем примере, с использованием уже существующего инструмента, unique_ptr, показывает как хороший ярлык для управления дескрипторами. Дополнительные предположения, которые он требует, заставляют его отстать. Правильные предположения состоят в том, что у вас есть дескриптор, и у вас есть функция уничтожения ресурсов, которая уничтожает ресурс, заданный дескриптором. Независимо от того, является ли дескриптор void *, a GLuint, что бы это ни было, это не имеет значения, и, что еще хуже, не нужно даже требовать сопоставить внутренний тип HANDLE. Для управления ручками RAII, если идиома говорит об этом хорошо и не может применяться в таких ситуациях, я считаю, что это не хорошо для этого, наконец, с данным инструментом.

РЕДАКТИРОВАТЬ 3

Иллюстративная ситуация была бы, скажем, вы отвечаете за использование новой сторонней библиотеки C. Он содержит FooHandle create_foo() и a void destroy_foo(FooHandle). Итак, вы думаете, "позвольте использовать FooHandle, используя правило нуля". Хорошо, вы идете с помощью unique_ptr, a unique_ptr к тому, что вы спрашиваете сами? Является FooHandle указателем?, так что вы используете unique_ptr to FooHandle не подвергаемый базовый тип? Это int?, так что вы можете использовать его прямо или лучше (re) typedef вещи, подобные @NicolBolas, сделали в его ответе. Для меня это кажется явным, даже в такой тривиальной ситуации, unique_ptr уже показывает, что он не идеально подходит для управления ручками ресурсов.

Отказ от ответственности:

Я попытался переформулировать и лучше выразить себя:

EDIT 4

Я нашел то, что искал, я поставил его как ответ в переформулированном вопросе: qaru.site/info/404574/....

4b9b3361

Ответ 1

Фон: Правило нуля

В первую очередь, эта статья посвящена гораздо более общей идее, чем только пример оболочки обложки ресурса, о которой он упоминает.

Дело в том, что всякий раз, когда класс содержит членов с "нетривиальной" семантикой собственности, класс должен не отвечать за механику за обеспечение правильной семантики значений ( копировать, назначать, перемещать, разрушать) содержащего класса. Скорее, каждый компонент должен сам реализовать "Правило-3 +", если это необходимо, так что составной класс может использовать специальные элементы, не соответствующие стандарту компилятора.

Кроме того, новые стандартные типы интеллектуальных указателей значительно упрощают задачу обеспечения этого для содержащихся типов. В большинстве случаев членами, которые требовали внимания, были бы указатели: std:: unique_ptr, shared_ptr, weak_ptr адресуют потребности здесь.

Для пользовательских типов ресурсов вам может понадобиться написать класс оболочки Rule-Of-3 +, но только один раз. И остальная часть ваших классов будет пользоваться Правилом Зеро.


Пули:

  • Один из них способен сверять и использовать базовый тип, на котором строится РУЧКА #define (или typedef). Для меня это должно быть скрытое знание, и решение должно основываться исключительно на том, что делает интерфейс, HANDLE.

A: 90% времени, когда вы можете (и должны) использовать std::unique_ptr<> с пользовательским делетером. Знания и ответственность за очистку по-прежнему связаны с распределителем. Как это должно быть.

В остальных случаях вам придется написать один класс-оболочку для вашего конкретного ресурса, который в противном случае не поддерживается.

  • Ручки могут быть любыми, они не должны быть типами указателей.

Они могут. Вы бы посмотрели, например. повышение:: необязательными. Или напишите эту обертку. Дело в том, что вы хотите выделить изолированный ресурс. Вы не хотите усложнять класс, который имеет собственный/содержащий такой ресурс.

Ответ 2

Является ли эта обертка, сфокусированная на RAII, уже сделанной и полезной в какой-нибудь классной библиотеке?

В вашем случае он называется unique_ptr. Обратите внимание:

struct WndLibDeleter
{
  typedef HANDLE pointer;

  void operator()(HANDLE h) {::FreeLibrary(h);}
};

using WndLibrary = std::unique_ptr<HANDLE, WndLibDeleter>;

WndLibrary LoadWndLibrary(std::wstring const& name)
{
  return WndLibrary(::LoadLibrary(name.c_str()));
}

С помощью удалений unique_ptr может обслуживать и обслуживать любые объекты, которые являются NullablePointer. HANDLE - это NullablePointer, поэтому вы можете его обслуживать.

Для объектов, которые не являются NullablePointers, вам придется использовать что-то еще. Точка правила "Нуль" должна сделать "что-то еще" как можно меньше. Это будет не что иное, как обертка RAII вокруг типа, предоставляющая не что иное, как доступ к ней и перемещение поддержки. Поэтому подавляющему большинству вашего кода не нужны явные конструкторы copy/move; только те немногие листовые классы.