# V746. Object slicing. An exception should be caught by reference rather than by value.

Анализатор обнаружил потенциальную ошибку, связанную с перехватом исключения по значению. Намного лучше и безопасней перехватывать исключение по ссылке.

Вообще, перехват исключения по значению порождает две разновидности ошибок. Рассмотрим их поочередно.

Проблема N1. Срезка (slicing).

    class Exception_Base {
    ....
    virtual void Print() { .... }
    };
    class Exception_Ex : public Exception_Base { .... };
    try
    {
      if (error) throw Exception_Ex(1, 2, 3);
    }
    catch (Exception_Base e)
    {
      e.Print();
      throw e;
    }

Объявлено 2 класса: исключение базового типа и расширенное исключение, которое наследуется от первого.

Генерируется расширенное исключение. Это исключение планируется перехватить, распечатать о нём информацию и пробросить исключение дальше.

Исключение перехвачено по значению. Это значит, что с помощью конструктора копирования будет сконструирован новый объект 'e' типа Exception_Base. Это порождает сразу 2 ошибки.

Во-первых, часть информации об исключении потеряна. Всё что хранилось Exception_Ex нам более недоступно. Виртуальная функция Print() позволит вывести базовую информацию о проблеме.

Во-вторых, дальше будет проброшено уже новое исключение типа Exception_Base. Таким образом мы передали дальше урезанную информацию о возникшей проблеме.

Правильно будет использовать следующий код:

    catch (Exception_Base &e)
    {
      e.Print();
      throw;
    }

Теперь функция Print() распечатает всю нужную информацию. Оператор "throw" будет пробрасывать далее уже существующее исключение, и информация не будет потеряна (срезана).

Проблема N2. Изменение временного объекта.

    catch (std::string s)
    {
      s += "Additional info";
      throw;
    }

Программист хочет перехватить исключение, добавить какую-то дополнительную информацию и пробросить это исключение дальше. Ошибка в том, что изменяется переменная 's', а оператор "throw;" пробрасывает далее исходное исключение. Таким образом мы не изменили информацию о исключении.

Правильный вариант:

    catch (std::string &s)
    {
      s += "Additional info";
      throw;
    }

Подробнее про преимущества перехвата исключения по ссылке можно прочитать здесь:

- Stack Overflow. C++ catch blocks - catch exception by value or reference?
- Stack Overflow. Catch exception by pointer in C++.
- Стефан К. Дьюхэрст. Скользкие места С++. Как избежать проблем при проектировании и компиляции ваших программ. – М.: ДМК Пресс. – 264 с.: ил. ISBN 5-94074-083-9.
- Wikipedia. Object slicing.

Данная диагностика классифицируется как:

CERT-ERR61-CPP

Взгляните на примеры ошибок, обнаруженных с помощью диагностики V746.