Каковы рекомендации по выбрасыванию исключения в стандартной функции C ++? - PullRequest
3 голосов
/ 08 октября 2011

Я обнаружил, что стандартные функции C ++ демонстрируют совершенно иное поведение при наличии исключения. Это, кажется, противоречит его рекламе "try / throw / catch". Кто-нибудь может, пожалуйста, кратко объяснить, почему разработчики C ++ обосновывают эти выборы?

  1. Ничего не делать, например, попытаться вытолкнуть () стек, когда он пуст (вместо броска range_error), выполнить sqrt (-1) (вместо броска domain_error)

  2. Возврат нулевого указателя: например, при недопустимом понижении указателя (интересно, при недопустимом понижении ссылочного значения выдается bad_cast)

  3. Создайте исключение, но это кажется меньшинству функций, например substr ()

  4. Дайте пользователю выбор, выбрасывать ли исключение, например, new () выбрасывает bad_alloc (), когда не хватает памяти, но вы также можете выбрать (nothrow) в качестве опции new ().

Ответы [ 5 ]

3 голосов
/ 08 октября 2011

Большая часть поведения библиотечных функций C ++ может быть объяснена общей философией C ++ «вы не платите за то, что не используете». Это означает, что при правильном использовании любой конкретной конструкции не должно быть ненужных накладных расходов.

При желании могут существовать более дорогие проверенные версии, такие как std::vector::at(), но решать вам, использовать их или нет.

Пример stack::pop() и sqrt() демонстрирует эту философию в действии: чтобы вызвать исключение при ошибке, вам всегда необходимо проверить, является ли вызов действительным. Эта проверка не нужна, если вы уже знаете, что ваш вызов будет успешным, поэтому в эти функции не встроена обязательная проверка. Если вы хотите чек, вы можете написать его самостоятельно.

Значение по умолчанию new немного отличается, поскольку оно включает в себя средства для вызова new_handler, и поэтому проверка выполняется в любом случае. (Вспомните, что исключение стоит дорого только в том случае, если вы его на самом деле выбрасываете, поэтому этот аспект не так важен.) Если вы хотите, вы всегда можете заменить свой собственный глобальный operator new() на тот, который буквально перенаправляет аргумент на * 1015. *. (Это, конечно, сделало бы небезопасным использование выражения new по умолчанию, поскольку теперь у вас нет возможности проверить, что вы можете создать объект по возвращенному указателю. Таким образом, вы в конечном итоге сами напишете проверку и будете использовать new, что почти точно делает nothrow-версия.)

2 голосов
/ 08 октября 2011

Указатель вниз, возвращающий ноль, является более простым и быстрым способом проверить, принадлежит ли данный объект данному подклассу. То есть Вы можете написать что-то вроде if (dynamic_cast<A*>(v) || dynamic_cast<B*>(v)) или if (A* a = dynamtic_cast<A*>(v)) doStuffWith(a);, что было бы громоздко, за исключением Здесь вы на самом деле ожидаете, что приведение не будет выполнено, в то время как исключения являются исключительными по своей природе, поскольку предполагается, что они редко генерируются во время нормального выполнения вашей программы.

В других случаях явные проверки на некорректные значения могут быть пропущены только из соображений производительности. Предполагается, что C ++ эффективен, вместо того, чтобы пытаться предотвратить выстрел в себя.

2 голосов
/ 08 октября 2011

Возврат нулевого указателя: например, при недопустимом понижении указателя (интересно, при недопустимом понижении ссылочного значения будет выбрасываться bad_cast)

dynamic_cast предоставляет способ проверитьсрок действия актерского состава.Это ни в коем случае не исключение как таковое. С указателями приведение возвращает NULL, потому что выбрасывание исключения будет непроизводительным, и то же самое может быть достигнуто путем возврата NULL, с ограничением, что References не может быть NULL, не было никакой возможности, кроме как генерировать исключение, в этом случае не было никакого другого способа вернуть результат пользователю.

Дать пользователю выбор, создавать ли исключение, например, new() будет выбрасывать bad_alloc (), когда не хватит памяти, но вы также можете выбрать (nothrow) в качестве опции new ().

Давным-давно new просто вернул NULL, как в случае malloc, но позже он был стандартизирован для выдачи исключения bad_alloc. Это означало бы, что весь код, ранее написанный с использованием new, пришлось бы в значительной степени модифицировать для обработки исключения, чтобы избежать этого и поддерживатьсовместимость была представлена ​​новая версия.

1 голос
/ 09 октября 2011

Стандартная библиотека c ++ разработана, чтобы быть эффективной, она по возможности избегает ненужных проверок во время выполнения.

1 Содержит нарушения предварительных условий для очень маленьких / быстрых методов, проверка этих предварительных условий, скорее всего, займет больше времени, чем выполнение самих методов (pop - это, скорее всего, один декремент в стеке простых типов).

2 dynamic_cast проверяет и приводит указанный указатель к совместимому типу. Не существует отдельного способа проверки только того, возможно ли приведение, поскольку оно должно выполнять ту же работу, что и приведение. Поскольку c ++ не предоставляет отдельного способа только проверять, возможно ли приведение, мы должны ожидать, что оно может дать сбой, и у него есть хорошее значение ошибки, которое можно использовать при сбое (NULL). Эталонная версия должна выдать исключение, поскольку она не может вернуть значение ошибки.

3 substr гарантирует исключение, это может быть по двум причинам. Первый: метод substr немного сложнее и медленнее, чем методы, упомянутые в 1, и поэтому накладные расходы на проверку предусловия незначительны. Второе: обработка строк является одним из основных факторов, влияющих на дыры в безопасности, поскольку вы, скорее всего, обрабатываете ввод данных пользователем, для обеспечения безопасности / стабильности процесса необходима проверка на переполнение или доступ через границы. Библиотека c предоставляет быстрые, непроверенные и небезопасные методы для работы со строками для тех, кому нужна скорость.

4 new должен проверить, может ли он вернуть адрес или дать сбой в обоих случаях, так как нехватка памяти неожиданна для большинства приложений, исключение является разумным. Однако вы можете писать на С ++, используя небольшое подмножество его функций, и многие проекты не используют исключения, так как сделать безопасным исключение для вашего кода сложно (особенно если вы используете сторонние библиотеки, которых нет), так как new является центральной частью c ++ и реализация без исключений становится необходимой.

1 голос
/ 08 октября 2011

Большая часть этого зависит от того, достаточно ли соответствующих возвращаемых значений для выражения условий "сбоя" и удобны ли эти значения.

Обоснование приведенных примеров

std::stack<>::pop()не выдает ошибку, поскольку не имеет значения ошибки и упрощает вызов кода.Возможно, это не логическая ошибка приложения при попытке вытолкнуть пустой стек.

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

dynamic_cast<> для типов указателей возвращает нулевой указатель, указывающий на неудачное приведение, поскольку нулевой указатель уже является стандартным способом представления «точек на ничто».Однако такого эквивалента для ссылок не существует, поэтому он должен вызвать исключение, поскольку возвращаемое значение не может быть использовано для указания ошибки.

В отличие от этого std::string::substr() не может вернутьПустая строка для обозначения ошибки в виде пустой строки является допустимой подстрокой всех строк.

new метание std::bad_alloc или, кажется, не имеет исторических корней, но, как и dynamic_cast<> в указателях,это может быть удобно для некоторого кода, который будет использовать альтернативы, чтобы не платить за обработку исключений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...