История изменений
Исправление slovazap, (текущая версия) :
Использовать исключения - хорошо, при соблюдении ряда условий.
- исключение - от слова исключительный, и должно использоваться соответственно, т.е. только в действительно неожиданных ситуациях. Простой пример обратного - mybitmap::setpixel мог бы кидать исключение при попытке нарисовать вне границ изображения. Однако это вполне легитимная операция, например (условно) в игре изображение персонажа может пересекать границу экрана. Кидать исключение здесь - означает что либо персонаж не будет отрисован (точнее, отрисован частично, до первого пикселя вне экрана), либо пользователю придётся руками обрезать изображение персонажа пересекающего границу, либо функция отрисовки будет обрабатывать исключение на каждый внеэкранный пиксель. Первое не приемлемо по соображениям качества, второе - по соображениям удобства пользователя библиотеки, третье - по соображениям производительности. А всё потому что это никакая не исключительная ситуация. А вот ошибка сохранения или чтения файла - совсем другое дело.
- обязательно наследоваться от std::exception и соблюдать его инварианты (throw() / noexcept what()). К слову, это адекватным образом возможно только начиная с C++11, где std::string::c_str noexcept - иначе можно будет хранить в исключении только тупые const char*.
- внимательно следить за exception safety (хотя это нужно делать всегда, ибо bad_alloc может кинуть что угодно). Опять же c++11 в этом сильно помогает, ибо с ним можно вообще не использовать new и обычные указатели.
- следить чтобы исключение не кинулось в деструкторе. Как правило это получается само собой, ибо освобождение ресурсов обычно не порождает ошибок, но бывают исключения. Например, гипотетический класс File закрывает файловый дескриптор в деструкторе, а close() может вернуть ошибку. В таких случаях обычно в деструкторе ошибку игнорируют, но делают дополнительный метод, который позволит ошибку таки обработать, если это нужно.
Например, если перед вызовом функции read() имя открываемого файла указано не было, выбрасывается исключение
Не представляю как можно спроектировать класс BMP файла так чтобы нельзя было указать имя файла. Либо это BMP::BMP(const std::string& filename), либо BMP::Read(const std::string& filename). Если же вы про проверку finename на "", то не нужно - не ваше дело что я хочу открыть. Неправильное имя файла обработает система, а лишние «умные» проверки порождают не кроссплатформенный код: откуда вы знаете что в моей систему пустая строка - не допустимое имя файла?
А вообще, если и проверять аргументы, то лучше через assert. Неправильный аргумент - всегда ошибка в коде, а исключение может тихо обработаться и ошибки вы не заметите. assert же громко падает и позволяет исправить ошибку, а из release сборки выкидывается, не вызывая накладных расходов.
Исправление slovazap, :
Использовать исключения - хорошо, при соблюдении ряда условий.
- исключение - от слова исключительный, и должно использоваться соответственно, т.е. только в действительно неожиданных ситуациях. Простой пример обратного - mybitmap::setpixel мог бы кидать исключение при попытке нарисовать вне границ изображения. Однако это вполне легитимная операция, например (условно) в игре изображение персонажа может пересекать границу экрана. Кидать исключение здесь - означает что либо персонаж не будет отрисован (точнее, отрисован частично, до первого пикселя вне экрана), либо пользователю придётся руками обрезать изображение персонажа пересекающего границу, либо функция отрисовки будет обрабатывать исключение на каждый внеэкранный пиксель. Первое не приемлемо по соображениям качества, второе - по соображениям удобства пользователя библиотеки, третье - по соображениям производительности. А всё потому что это никакая не исключительная ситуация. А вот ошибка сохранения или чтения файла - совсем другое дело.
- обязательно наследоваться от std::exception и соблюдать его инварианты (throw() / noexcept what()). К слову, это адекватным образом возможно только начиная с C++11, где std::string::c_str noexcept - иначе можно будет хранить в исключении только тупые const char*.
- внимательно следить за exception safety (хотя это нужно делать всегда, ибо bad_alloc может кинуть что угодно). Опять же c++11 в этом сильно помогает, ибо с ним можно вообще не использовать new и обычные указатели.
- следить чтобы исключение не кинулось в деструкторе. Как правило это получается само собой, ибо освобождение ресурсов обычно не порождает ошибок, но бывают исключения. Например, гипотетический класс File закрывает файловый дескриптор в деструкторе, а close() может вернуть ошибку. В таких случаях обычно в деструкторе ошибку игнорируют, но делают дополнительный метод, который позволит ошибку таки обработать, если это нужно.
Например, если перед вызовом функции read() имя открываемого файла указано не было, выбрасывается исключение
Не представляю как можно спроектировать класс BMP файла так чтобы нельзя было указать имя файла. Либо это BMP::BMP(const std::string& filename), либо BMP::Read(const std::string& filename). Если же вы про проверку finename на "", то не нужно - не ваше дело что я хочу открыть. Ошибку обработает система, а лишние «умные» проверки порождают не кроссплатформенный код: откуда вы знаете что нет системы разрешающей пустые названия файлов?
А вообще, если и проверять аргументы, то лучше через assert. Неправильный аргумент - всегда ошибка в коде, а исключение может тихо обработаться и ошибки вы не заметите. assert же громко падает и позволяет исправить ошибку, а из release сборки выкидывается, не вызывая накладных расходов.
Исходная версия slovazap, :
Использовать исключения - хорошо, при соблюдении ряда условий.
- исключение - от слова исключительный, и должно использоваться соответственно, т.е. только в действительно неожиданных ситуациях. Простой пример - mybitmap::setpixel мог бы кидать исключение при попытке нарисовать вне границ изображения. Однако это вполне легитимная операция, например (условно) в игре изображение персонажа может пересекать границу экрана. Кидать исключение здесь - означает что либо персонаж не будет отрисован (точнее, отрисован частично, до первого пикселя вне экрана), либо пользователю придётся руками обрезать изображение персонажа пересекающего границу, либо функция отрисовки будет обрабатывать исключение на каждый внеэкранный пиксель. Первое не приемлемо по соображениям качества, второе - по соображениям удобства пользователя библиотеки, третье - по соображениям производительности. А всё потому что это никакая не исключительная ситуация. А вот ошибка сохранения или чтения файла - совсем другое дело.
- обязательно наследоваться от std::exception и соблюдать его инварианты (throw() / noexcept what()). К слову, это адекватным образом возможно только начиная с C++11, где std::string::c_str noexcept - иначе можно будет хранить в исключении только тупые const char*.
- внимательно следить за exception safety (хотя это нужно делать всегда, ибо bad_alloc может кинуть что угодно). Опять же c++11 в этом сильно помогает, ибо с ним можно вообще не использовать new и обычные указатели.
- следить чтобы исключение не кинулось в деструкторе. Как правило это получается само собой, ибо освобождение ресурсов обычно не порождает ошибок, но бывают исключения. Например, гипотетический класс File закрывает файловый дескриптор в деструкторе, а close() может вернуть ошибку. В таких случаях обычно в деструкторе ошибку игнорируют, но делают дополнительный метод, который позволит ошибку таки обработать, если это нужно.
Например, если перед вызовом функции read() имя открываемого файла указано не было, выбрасывается исключение
Не представляю как можно спроектировать класс BMP файла так чтобы нельзя было указать имя файла. Либо это BMP::BMP(const std::string& filename), либо BMP::Read(const std::string& filename). Если же вы про проверку finename на "", то не нужно - не ваше дело что я хочу открыть. Ошибку обработает система, а лишние «умные» проверки порождают не кроссплатформенный код: откуда вы знаете что нет системы разрешающей пустые названия файлов?
А вообще, если и проверять аргументы, то лучше через assert. Неправильный аргумент - всегда ошибка в коде, а исключение может тихо обработаться и ошибки вы не заметите. assert же громко падает и позволяет исправить ошибку, а из release сборки выкидывается, не вызывая накладных расходов.