2015-02-26 2 views
2

В C++, если метод может бросать только потому, что инварианты класса не поддерживаются, следует ли его пометить noexcept?Инварианты и noexcept

Например, в списке есть указатель на ссылку, которая должна быть либо nullptr, либо правильный указатель, а метод разыгрывает этот указатель. Предотвращает ли это использование noexcept?

+0

Возможный UB не предотвращает использование 'noexcept'. –

+1

Вызов недопустимого указателя не определен и не вызывает исключения на C++. – molbdnilo

+0

@molbdnilo Поскольку это неопределенное, это _might_ вызывает исключение C++. Опять же, это может и не быть; это может просто испортить арену свободного пространства, поэтому ваша программа вылетает в совершенно несвязанном месте. (Visual Studios имеет возможность конвертировать доступ за пределами отображаемой памяти в исключение, но, конечно, недопустимый указатель не обязательно должен указывать за пределами отображаемой памяти. Таким образом, вы не можете писать код, который зависит от этого исключения.И в большинстве случаев вы захотите прервать, а не выбросить исключение в любом случае.) –

ответ

0

Если функция может выйти через исключение, вы не должны пометить его nothrow; nothrow является гарантией, что это никогда выходов через исключение. Очень важно, чтобы было хотя бы несколько функций, которые никогда не выбрасывают (и я имею в виду никогда), если вы хотите написать безопасный код исключения.

Если инварианты класса не могут быть сохранены, вы не должны выбрасывать исключение ; все, что вы можете разумно сделать, это прервать процесс. Не путайте это с конструктором, который не может установить инварианты, однако; его штраф бросать тогда, так как после этого не будет объекта , который не соответствует инвариантам. Кроме того, в некоторых случаях справедливо определить более слабый набор инвариантов, которые сохраняются после метаданных . Скажите достаточно, чтобы гарантировать безопасное удаление объекта . Это зависит от приложения (и того, как дизайн обрабатывает исключения ). Но такие функции не могут быть объявлены nothrow.

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

0

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

Вызывает ли это исключение или нет, также является хорошей практикой, что объект сохраняет свой инвариант. Обычная практика заключается в том, что все конструкторы устанавливают инвариант, а все остальные функции-члены поддерживают этот инвариант, пока объект не будет уничтожен (и вызван деструктор).

Если функция разыгрывает nullptr, ее поведение не определено. Не нужно бросать исключение.

0

Если метод может вызывать исключение при разыменовании нулевого указателя, он не должен быть помечен noexcept. Если вы действительно хотите, чтобы это было noexcept, вы должны проверить указатель на нуль (и вернуть специальное значение) перед разыменованием его. Например

if (pt == NULL) return NULL; 
// do use *pt or pt->xxx 
+1

Функция проверяет, что указатель не является нулевым и разыскивает его. И инвариант в том, что указатель либо нуль, либо правильный. Тогда, если это неверно, это неуправляемое поведение? Таким образом, он выбрасывается только из-за UB и может быть отмечен как-то? – statnik

+0

@statnik Я, вероятно, вообще не бросаю. Невозможно (или почти) определить, действителен ли указатель или нет; если вы пишете недопустимый указатель, наиболее распространенным результатом является повреждение какого-либо другого объекта или повреждение арены свободного пространства. Оба из них могут привести к сбою программы через некоторое время, в абсолютно несвязанный код. –