C ++: нарезка объектов и исключения

В одном из интервью меня спросили, почему перехват исключений по значению может быть проблемой, и я ответил, что это может вызвать нарезку объектов. И это то, что я нахожу в Интернете, например, здесь: 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, и все работает нормально, никаких срезов.
Как так?

А может кто-нибудь привести пример нарезки при ловле по значению?

3

Решение

Даже если 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),

В заключение я бы придерживался «Бросить по значению, поймать (постоянную) ссылку)». В этом конкретном примере вы хорошо ловите по разрезанному значению, но это не совсем так. Серж Баллеста ответ приведите пример улова по значению, приводящего к неприятностям. Быстрый поиск в вашей любимой поисковой системе может помочь вам найти другие случаи, когда поиск по значению приводит к проблемам.

5

Другие решения

Еще одна проблема отлова исключения по значению состоит в том, что для него требуется полная копия исключения. Если вы близки к условию StackOverflow (или уже обрабатываете его), вы можете оказаться в ситуации, когда копирование невозможно, и предложение catch не может быть выполнено.

1

По вопросам рекламы [email protected]