Действительно ли использование ScopeGuard приводит к улучшению кода?

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

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

Решение

Это определенно улучшит ваш код.Ваше предварительно сформулированное утверждение о том, что оно неясно и что код заслуживает catch Блок просто неверен в C++, поскольку RAII — это устоявшаяся идиома.Обработка ресурсов в C++ является выполняется путем получения ресурсов, а сбор мусора осуществляется неявными вызовами деструктора.

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

RAII (включая ScopeGuards) — это не малоизвестный метод C++, а прочно устоявшаяся передовая практика.

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

Да.

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

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

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

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

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

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

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

Да.

Это было настолько важно в C++, что даже в 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
}

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

Если охранники области имеют какое-либо реальное применение, обработка ресурсов нет один из них.В этом случае они значительно уступают простому RAII, поскольку RAII дедуплицируется и работает автоматически, а защита области — это ручное дублирование кода или блокировка.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top