четверг, 22 марта 2012 г.

Исключение надо всегда перехватывать по ссылке!

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

#include <exception>
#include <string>
#include <iostream>
class MyException : public std::exception {
public:
    MyException(const std::string & msg)
        : std::exception(), m_msg(msg)
    {
    }
    const char * what() const throw()
    {
        return m_msg.c_str();
    }
    ~MyException() throw()
    {
    }
private:
    std::string m_msg;
};
 
 
int main()
{
    try
    {
        throw MyException("MyException");
    }
    catch(std::exception e) //вот тут происходит т.н. "расщепление"
    {
        std::cout << e.what();
    }
    return 0;
}


Этот код выведет "std::exception". Это происходит потому, что в строке с инструкцией catch(std::exception e) происходит преобразование к значению базового типа (т.н. "срезка" или "расщепление"), в котором нет строки-члена  m_msg, который хранит сообщение об ошибке. То есть, до блока обработки долетает совсем не то, что было сгенерировано внутри try. И выводится именно то, что выводится в std::exception::what(), а не MyException::what(). Чтобы получать правильный тип исключения, надо получать значение по ссылке. То есть заменить "catch(std::exception e)" на "catch(std::exception & e)". Тогда не происходит преобразования к значению класса базового типа и корректно работает механизм полиморфизма. Как с указателями. И в результате мы получаем именно тот объект-исключение, который был сгенерирован в блоке try.
На самом деле не вижу ни одной причины, чтобы не использовать перехват исключений по ссылке всегда. Перехват по значению может привести к описанным выше проблемам, а перехват по указателю обычно ведет к необходимости удалять объект исключения после его  обработки. А это лишнее действие, которое можно и забыть сделать. Поэтому проще всего перехватывать исключения по ссылке и не забивать себе голову лишними действиями. Ну а генерировать исключения надо, в таком случае, всегда по значению.

Комментариев нет:

Отправить комментарий