Я только что исправил очень тонкую ошибку в нашем коде, вызванную нарезкой исключения, и теперь хочу убедиться, что я точно понимаю, что происходит.
Вот наш базовый класс исключений, производный класс и соответствующие функции:
class Exception
{
public:
// construction
Exception(int code, const char* format="", ...);
virtual ~Exception(void);
<snip - get/set routines and print function>
protected:
private:
int mCode; // thrower sets this
char mMessage[Exception::MessageLen]; // thrower says this FIXME: use String
};
class Derived : public Exception {
public:
Derived (const char* throwerSays) : Exception(1, throwerSays) {};
};
void innercall {
<do stuff>
throw Derived("Bad things happened!");
}
void outercall {
try {
innercall();
}
catch(Exception& e)
{
printf("Exception seen here! %s %d\n", __FILE__, __LINE__);
throw e;
}
}
Ошибка, конечно же, заключалась в том, что внешний вызов вызывал исключение вместо производного. Моя ошибка возникла из-за того, что на более высоком уровне стека вызовов не удалось поймать Derived.
Теперь я просто хочу убедиться, что понимаю - я считаю, что в строке 'throw e' создается новый объект Exception с использованием конструктора копирования по умолчанию. Это то, что происходит на самом деле?
Если да, могу ли я заблокировать конструкторы копирования для объектов, которые будут выброшены? Я бы действительно предпочел, чтобы это не повторилось, и у нашего кода нет причин копировать объекты Exception (о которых я знаю).
Не комментируйте, пожалуйста, то, что у нас своя иерархия исключений. Это немного устаревший дизайн, над которым я работаю (я добиваюсь значительных успехов. Я избавился от доморощенного класса струн и многих самодельных контейнеров).
ОБНОВЛЕНИЕ: Для ясности, я исправил ошибку (изменив «throw e» на «throw») еще до того, как задал вопрос. Я просто искал подтверждения тому, что происходило.
4 ответа
Когда вы бросаете объект, вы фактически бросаете его копию, а не оригинал. Подумайте об этом - исходный объект находится в стеке, но стек раскручивается и становится недействительным.
Я считаю, что это часть стандарта, но у меня нет копии для ссылки.
Тип исключения, создаваемого в блоке catch, является базовым типом catch, а не типом сгенерированного объекта. Способ решения этой проблемы состоит в том, чтобы throw;
, а не throw e;
, который вызовет исходное пойманное исключение.
C ++ не перестает меня удивлять. Я бы потерял много денег, если бы поставил на то, каково было поведение!
Объект исключения сначала копируется во временный, и вы должны были использовать throw
. Процитируем стандарт 15.1 / 3:
Выражение throw инициализирует временный объект, называемый объектом исключения, тип которого определяется удалением любых cv-квалификаторов верхнего уровня из статического типа операнда throw и корректировкой типа из "массива T" или " функция, возвращающая T "на" указатель на T "или" указатель на функцию, возвращающую T "соответственно.
Я думаю, это дает начало очень полезному стандартному правилу кодирования:
Базовые классы иерархии исключений должны иметь чистый виртуальный деструктор.
Или
Конструктор копирования для базового класса в иерархии исключений должен быть защищен.
Либо достигается цель, о которой компилятор предупредит, когда вы попытаетесь «выбросить e», поскольку в первом случае вы не можете создать экземпляр абстрактного класса, а во втором - потому что вы не можете вызвать конструктор копирования.
Да.
throw e;
Генерирует исключение статического типа e
, независимо от того, чем на самом деле является e
. В этом случае исключение Derived
копируется в Exception
с помощью конструктора копирования.
В этом случае вы можете просто
throw;
Чтобы корректно всплыло всплывающее окно исключения Derived
.
Если вас интересует полиморфный бросок в некоторых других случаях, обратитесь к Всегда такой полезный C ++ FAQ Lite.
Быстрый поиск в Google подсказывает, что да, вы создаете конструктор копирования, который является обязательным и должен быть общедоступным. (Что имеет смысл, поскольку вы инициализируете копию e
и бросаете ее.)
В любом случае, просто используйте throw
без указания объекта исключения, чтобы повторно выбросить то, что было перехвачено в catch
. Разве это не должно решить проблему?
catch(Exception& e)
{
printf("Exception seen here! %s %d\n", __FILE__, __LINE__);
throw;
}
Похожие вопросы
Связанные вопросы
Новые вопросы
c++
C ++ - это язык программирования общего назначения. Первоначально он был разработан как расширение C и имеет аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде (который должен быть) скомпилирован с помощью компилятора C ++. Используйте тег для конкретной версии для вопросов, связанных с конкретной версией стандарта [C ++ 11], [C ++ 14], [C ++ 17], [C ++ 20] или [C ++ 23] и т. Д. .