Использует ли ScopeGuard действительно лучший код? - PullRequest
32 голосов
/ 07 сентября 2008

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

Ответы [ 7 ]

61 голосов
/ 07 сентября 2008

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

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

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

29 голосов
/ 15 февраля 2009

Да.

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

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

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

2 голосов
/ 08 сентября 2008

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

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
1 голос
/ 21 октября 2016

Да.

Это было так важно в 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
}
1 голос
/ 11 января 2016

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

1 голос
/ 07 сентября 2008

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

0 голосов
/ 04 ноября 2015

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

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

...