C++ исключение паранойя безопасности: сколько это слишком много?
Строгая гарантия безопасности исключений говорит о том, что операция не изменит никакого состояния программы в случае возникновения исключения. Элегантный способ реализации безопасного копирования-назначения - это идиома копирования и замены.
Мои вопросы:
Было бы излишним использовать копирование и обмен для каждой мутирующей операции класса, который мутирует не примитивные типы?
Является ли производительность действительно честной сделкой для сильной исключительной безопасности?
Например:
class A
{
public:
void increment()
{
// Copy
A tmp(*this);
// Perform throwing operations on the copy
++(tmp.x);
tmp.x.crazyStuff();
// Now that the operation is done sans exceptions,
// change program state
swap(tmp);
}
int setSomeProperty(int q)
{
A tmp(*this);
tmp.y.setProperty("q", q);
int rc = tmp.x.otherCrazyStuff();
swap(tmp);
return rc;
}
//
// And many others similarly
//
void swap(const A &a)
{
// Non-throwing swap
}
private:
SomeClass x;
OtherClass y;
};
4 ответа
Как и все вопросы техники, речь идет о балансе.
Конечно, const
-нессность / неизменность и сильные гарантии повышают уверенность в своем коде (особенно в сопровождении тестов). Они также помогают сократить пространство возможных объяснений для ошибки.
Тем не менее, они могут повлиять на производительность.
Как и все проблемы с производительностью, я бы сказал: профиль и избавиться от горячих точек. Copy And Swap, безусловно, не единственный способ достижения транзакционной семантики (он просто самый простой), поэтому профилирование подскажет вам, где вы не должны его использовать, и вам придется искать альтернативы.
Вы всегда должны стремиться к базовой гарантии исключения: убедитесь, что в случае исключения все ресурсы высвобождаются правильно и объект находится в допустимом состоянии (которое может быть неопределенным, но допустимым).
Гарантия строгого исключения (т. Е. "Транзакции") - это то, что вы должны реализовать, когда считаете, что это имеет смысл: вам не всегда нужно транзакционное поведение.
Если легко выполнить транзакционные операции (например, с помощью копирования и обмена), сделайте это. Но иногда это не так, или это оказывает большое влияние на производительность, даже для фундаментальных вещей, таких как операторы присваивания. Я помню, как реализовывал что-то вроде boost:: option, где я не всегда мог обеспечить надежную гарантию при назначении копии.
Одна огромная трудность, с которой вы столкнетесь, связана с семантикой перемещения. Вы хотите транзакции при перемещении, потому что в противном случае вы потеряете перемещенный объект. Однако вы не всегда можете предоставить надежную гарантию: подумайте о std::pair<movable_nothrow, copyable>
(и посмотрите комментарии). Это где вы должны стать noexcept
виртуоз и использовать неудобное количество метапрограммирования. С ++ сложно освоить именно из-за исключительной безопасности.
Это зависит от того, в какой среде будет работать ваше приложение. Если вы просто запустите его на своем собственном компьютере (один конец спектра), возможно, не стоит слишком строго соблюдать требования безопасности исключений. Если вы пишете программу, например, для медицинских устройств (другой конец), вы не хотите, чтобы непреднамеренные побочные эффекты оставались позади при возникновении исключения. Все промежуточное зависит от уровня терпимости к ошибкам и доступных ресурсов для развития (время, деньги и т. Д.)
Да, проблема, с которой вы сталкиваетесь, заключается в том, что эту идиому очень трудно масштабировать. Ни один из других ответов не упомянул об этом, но другая очень интересная идиома, изобретенная Александреску, называлась scopeGuards. Это помогает повысить экономичность кода и значительно повышает удобочитаемость функций, которые должны соответствовать строгим гарантиям безопасности исключений.
Идея защиты области видимости - это экземпляр стека, который позволяет просто прикрепить объекты функции отката к каждому получению ресурса. когда ограждение области действия уничтожено (исключением), выполняется откат. Вам нужно явно позвонить commit()
в нормальном потоке, чтобы избежать вызова отката при выходе из области видимости.
Проверьте этот недавний вопрос от меня, который связан с разработкой безопасной области наблюдения с использованием функций C++11.