>
>
>
V746. Object slicing. An exception shou…


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;
}

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

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

  • CERT-ERR61-CPP

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