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

Использует ли ScopeGuard действительно лучший код?

Я встретил эту статью, написанную Андреем Александреску и Петром Маргинеем много лет назад, в которой представлен и обсуждается класс утилиты ScopeGuard для написания исключения - безопасный код. Я хотел бы знать, действительно ли кодирование с этими объектами приводит к лучшему коду или если оно запутывает обработку ошибок, возможно, обратный вызов защитника будет лучше представлен в блоке catch? У кого-нибудь есть опыт использования этих данных в фактическом производственном коде?

4b9b3361

Ответ 1

Он определенно улучшает ваш код. Ваше предварительно сформулированное утверждение, что оно неясное и что код будет достоин от блока catch, просто не является истинным в С++, потому что RAII является установленной идиомой. Обработка ресурсов в С++ выполняется путем сбора ресурсов, а сбор мусора выполняется неявными вызовами деструктора.

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

RAII (включая ScopeGuard s) не является неясной техникой в ​​С++, но прочно установила наилучшую практику.

Ответ 2

Да.

Если есть один кусок кода на С++, который я мог бы порекомендовать каждому программисту на С++ потратить 10 минут обучения, это ScopeGuard (теперь часть свободно доступного Loki библиотека).

Я решил попробовать (слегка измененную) версию ScopeGuard для небольшой программы Win32 GUI, над которой я работал. Win32, как вы знаете, имеет много разных типов ресурсов, которые необходимо закрыть по-разному (например, дескрипторы ядра обычно закрываются с помощью CloseHandle(), GDI BeginPaint() должен быть сопряжен с EndPaint() и т.д.). Я использовал ScopeGuard со всеми этими ресурсами, а также для распределения рабочих буферов с помощью new (например, для преобразования набора символов в/из Юникода).

Меня поразило, насколько короче была программа. В принципе, это беспроигрышный: ваш код становится короче и надежнее в одно и то же время. Будущие изменения кода не могут ничего протекать. Они просто не могут. Насколько это круто?

Ответ 3

Я часто использую его для защиты использования памяти, вещи, которые необходимо освободить, которые были возвращены из ОС. Например:

DATA_BLOB blobIn, blobOut;
blobIn.pbData=const_cast<BYTE*>(data);
blobIn.cbData=length;

CryptUnprotectData(&blobIn, NULL, NULL, NULL, NULL, CRYPTPROTECT_UI_FORBIDDEN, &blobOut);
Guard guardBlob=guardFn(::LocalFree, blobOut.pbData);
// do stuff with blobOut.pbData

Ответ 4

Я не использовал этот шаблон, но раньше я использовал что-то подобное. Да, это приводит к более четкому коду по сравнению с одинаково надежным кодом, реализованным по-разному.

Ответ 5

Я думаю, что выше ответов не хватает одной важной заметки. Как указывали другие, вы можете использовать ScopeGuard, чтобы освободить выделенные ресурсы независимо от отказа (исключения). Но это может быть не единственное, что вы можете использовать для защиты области. На самом деле, примеры в связанной статье используют ScopeGuard для другой цели: трансакции. Короче говоря, было бы полезно, если бы у вас было несколько объектов (даже если эти объекты правильно использовали RAII), которые вам нужно сохранить в состоянии, которое каким-то образом коррелировало. Если изменение состояния любого из этих объектов приводит к исключению (которое, я полагаю, обычно означает, что его состояние не изменилось), то все изменения, которые уже были применены, необходимо отменить. Это создает собственный набор проблем (что, если откат также не удался?). Вы можете попытаться развернуть свой собственный класс, который управляет такими коррелированными объектами, но по мере того, как число их увеличивается, оно будет беспорядочным, и вы, вероятно, вернетесь к использованию внутри ScopeGuard в любом случае.

Ответ 6

Да.

В С++ это было так важно, что даже специальный синтаксис для него в D:

void somefunction() {
    writeln("function enter");
    // c++ has similar constructs but not in syntax level
    scope(exit) writeln("function exit");

    // do what ever you do, you never miss the function exit output
}

Ответ 7

Я должен сказать, нет, нет, это не так. Ответы здесь помогают продемонстрировать, почему это поистине ужасная идея. Обработка ресурсов должна выполняться через повторно используемые классы. Единственное, что они достигли с помощью защиты области, - это нарушить DRY вверх по wazoo и дублировать их код освобождения ресурсов на всем протяжении своей кодовой базы вместо того, чтобы писать один класс для обработки ресурса, а затем его для всей партии.

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