Я не совсем уверен, когда именно
следует стремиться к исключительной безопасности против
скорость
Вы всегда должны стремиться к исключительной безопасности. Обратите внимание, что «безопасность исключений» не означает «выбросить исключение, если что-то пойдет не так». Это означает «предоставление одной из трех исключительных гарантий: слабая, сильная или нет». Бросать исключения не обязательно. Исключительная безопасность необходима для того, чтобы вызывающие абоненты вашего кода были уверены, что их код может работать правильно при возникновении ошибок.
Вы увидите очень разные стили от разных программистов / команд C ++ относительно исключений. Некоторые часто их используют, другие - совсем нет (или даже не совсем), хотя я думаю, что сейчас это довольно редко. Вероятно, Google является наиболее (не) известным примером. Если вам интересно, ознакомьтесь с их руководством по стилю C ++. Встраиваемые устройства и внутренности игр, вероятно, являются следующими наиболее вероятными местами, где можно найти примеры людей, избегающих исключений полностью в C ++). Стандартная библиотека iostreams позволяет вам установить флаг для потоков, должны ли они генерировать исключения при возникновении ошибок ввода-вывода. По умолчанию используется значение , а не to, что является сюрпризом для программистов практически из любого другого языка, в котором существуют исключения.
Должен ли я действительно выдавать ошибку при сбое списка?
Это не сбой "списка", это, в частности, pop_front
, который вызывается, когда список пуст, что не удается. Вы не можете обобщить все операции над классом, чтобы они всегда выдавали исключения при сбое, вы должны учитывать конкретные случаи. В этом случае у вас есть как минимум пять разумных вариантов:
- возвращает значение, чтобы указать, было ли что-либо вытолкнуто. Абонент может делать с ними все что угодно или игнорировать.
- документирует, что это неопределенное поведение - вызывать
pop_front
, когда список пуст, а затем игнорировать возможность в коде для pop_front
. Нужно убрать пустой стандартный контейнер, и некоторые реализации стандартной библиотеки не содержат проверочного кода, особенно в сборках релиза.
- документ, подтверждающий, что это неопределенное поведение, но в любом случае выполните проверку и либо отмените программу, либо сгенерируйте исключение. Возможно, вы могли бы выполнять проверку только в отладочных сборках (для этого
assert
), и в этом случае у вас также может быть возможность запуска точки останова отладчика.
- документ, подтверждающий, что если список пуст, вызов не действует.
- документ, который генерирует исключение, если список пуст.
Все это, кроме последнего, означает, что ваша функция может предложить гарантию "nothrow". Какой из них вы выберете, зависит от того, как вы хотите, чтобы ваш API выглядел, и какую помощь вы хотите дать своим посетителям в поиске их ошибок. Обратите внимание, что исключение не заставляет вашего непосредственного абонента его поймать. Исключения следует перехватывать только в коде, который способен восстановиться после ошибки (или, возможно, в самой верхней части программы).
Лично я склоняюсь к , а не , выдавая исключения для пользовательских ошибок, и я также склоняюсь к тому, чтобы сказать, что появление пустого списка - это ошибка пользователя. Это не означает, что в режиме отладки бесполезно иметь все виды проверок, просто я обычно не определяю API, чтобы гарантировать, что такие проверки будут выполняться во всех режимах.
Действительно ли для такой вещи нужен специальный класс ошибок
Нет, это не необходимо , потому что это ошибка, которую можно избежать. Вызывающий всегда может убедиться, что он не будет выдан, проверив, что список не пуст, перед вызовом pop_front
. std::logic_error
было бы вполне разумным исключением для броска. Основная причина использования специального класса исключений заключается в том, что вызывающие абоненты могут перехватить именно это исключение: вы сами решаете, должны ли вызывающие абоненты делать это для конкретного случая.
Возможно ли, чтобы голова _-> prev всегда бросала при назначении на 0?
Нет, если ваша программа как-то не спровоцировала неопределенное поведение.Так что да, вы можете уменьшить размер до этого, и вы можете уменьшить его до delete
, если вы уверены, что деструктор ListElem не может выбросить.И при написании любого деструктора, вы должны убедиться, что он не выдает.
Я часто слышу, что в программе на C ++ может произойти что-то неудачное.Реально ли проверить, не завершился ли конструктор для ListElem (или tail_ при обновлении)?
Это неправда, что все может потерпеть неудачу.В идеале функции должны документировать, какую гарантию исключения они предлагают, что, в свою очередь, говорит вам, могут ли они бросить или нет.Если они действительно хорошо документированы, они перечислят все, что они могут бросить, и при каких обстоятельствах они бросают это.
Вы не должны проверять лиСбой new
, вы должны разрешить исключению из new
, если таковое имеется, распространяться от вашей функции к вашему вызывающему.Затем вы можете просто задокументировать, что push_front
может выдать std::bad_alloc
, чтобы указать на нехватку памяти, и, возможно, также, что он может выбросить все, что было сгенерировано конструктором копирования T
(ничего, в случае int
).Возможно, вам не придется документировать это отдельно для каждой функции - иногда достаточно общего примечания, охватывающего несколько функций.Никого не должно удивлять, что если функция с именем push_front
может выдавать, то одна из вещей, которую она может сгенерировать, это bad_alloc
.Для пользователей контейнера шаблона не должно быть сюрпризом, что если содержащиеся элементы выдают исключения, то эти исключения могут распространяться.
Будет ли когда-либо необходимо проверять тип данных (в настоящее времяпростой typedef int T, пока я все не буду шаблонизировать), чтобы убедиться, что тип является жизнеспособным для структуры?
Вы, вероятно, можете написать свою структуру так, чтобы все, что требуется от T, заключалось в том, что она является копируемойпереуступка.Для этого не нужно добавлять специальные тесты - если кто-то попытается создать экземпляр вашего шаблона с типом, который не поддерживает операции, которые вы над ним выполняете, он получит ошибку компиляции.Вы должны документировать требования.