В одном из интервью меня спросили, почему перехват исключений по значению может быть проблемой, и я ответил, что это может вызвать нарезку объектов. И это то, что я нахожу в Интернете, например, здесь: https://www.viva64.com/en/w/v746/
Но сейчас я пытаюсь экспериментировать и не могу найти пример нарезки при ловле по значению. Обычный сценарий нарезки (без исключений) таков:
Derived d1;
Derived d2;
Base& b1 = d1;
Base& b2 = d2;
b1 = b2;
В последней строке вызывается оператор присваивания Base, который копирует только базовую часть производного объекта. Таким образом, основанная часть b1 копируется из d2, а производная часть b1 остается из d2. ПЛОХОЙ.
Но как это может произойти при отлове исключений по значению?
Я попробовал этот код (с обоими компиляторами: g ++ и Sun CC):
struct Base
{
virtual void print() const
{
cout << "{ Base: " << m << " }" << endl;
}
Base(int _m = 0) : m(_m) {}
int m;
};
struct Derived : Base
{
Derived(int _m = 0, int _n = 0) : Base(_m), n(_n) {}
void print() const
{
cout << "{ Base: " << m << ", Derived: " << n << " }" << endl;
}
int n;
};
int main()
{
try
{
try
{
throw Derived(3, 300);
}
catch(Base x)
{
cout << "Inner catch: ";
x.print();
throw;
}
}
catch(Derived y)
{
cout << "Outer catch: ";
y.print();
}
}
Выход был:
Inner catch: { Base: 3 }
Outer catch: { Base: 3, Derived: 300 }
Поэтому я выбрасываю исключение Derived, перехватываю его Base BY VALUE и перебрасываю, затем перехватываю Derived BY VALUE, и все работает нормально, никаких срезов.
Как так?
А может кто-нибудь привести пример нарезки при ловле по значению?
Даже если catch(Base)
сделать нарезку Derived
объект, реthrow
использует оригинальный объект исключения вместо нарезанной копии.
От http://en.cppreference.com/w/cpp/language/throw:
Отбрасывает текущее обработанное исключение. Отменяет выполнение текущего блока catch и передает управление следующему соответствующему обработчику исключений (но не другому предложению catch после того же блока try: его составной оператор считается «завершенным»), повторное использование существующего объекта исключения: новые объекты не создаются. Эта форма разрешена только в том случае, если в настоящее время обрабатывается исключение (она вызывает std :: terminate, если используется иначе). Предложение catch, связанное с функцией-try-block, должно завершиться с помощью rethrowing, если оно используется в конструкторе.
Обратите внимание, что если вы замените throw;
от throw x;
, Base
экземпляр будет брошен и не будет пойман, в результате чего std::abort()
быть названным. Действительно, нарезанный Derived
не может быть без нарезки (именно поэтому мы обычно не любим срезы, если они не пицца ломтики) быть пойманным catch (Derived)
,
В заключение я бы придерживался «Бросить по значению, поймать (постоянную) ссылку)». В этом конкретном примере вы хорошо ловите по разрезанному значению, но это не совсем так. Серж Баллеста ответ приведите пример улова по значению, приводящего к неприятностям. Быстрый поиск в вашей любимой поисковой системе может помочь вам найти другие случаи, когда поиск по значению приводит к проблемам.
Еще одна проблема отлова исключения по значению состоит в том, что для него требуется полная копия исключения. Если вы близки к условию StackOverflow (или уже обрабатываете его), вы можете оказаться в ситуации, когда копирование невозможно, и предложение catch не может быть выполнено.