Вопрос по object-slicing, exception, c++ – Исключение нарезки - это связано с созданным конструктором копирования?

10

Я только что исправил очень тонкую ошибку в нашем коде, вызванную нарезкой исключения, и теперь я хочу убедиться, что я точно понимаю, что происходит.

Вот наш базовый класс исключений, производный класс и соответствующие функции:

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

Ошибка, конечно, заключалась в том, что externalcall заканчивал тем, что выбрасывал исключение вместо производного. Моя ошибка возникла из-за того, что в стеке вызовов больше не было попыток отловить Derived.

Теперь я просто хочу убедиться, что я понимаю - я считаю, что на «бросить е»; В строке создается новый объект Exception с использованием конструктора копирования по умолчанию. Это действительно происходит?

Если да, могу ли я заблокировать конструкторы копирования для объектов, которые будут выброшены? Я действительно предпочел бы, чтобы это больше не повторилось, и у нашего кода нет причин копировать объекты Exception (о которых я знаю).

Пожалуйста, не комментируйте тот факт, что у нас есть собственная иерархия исключений. Это немного старомодный дизайн, над которым я работаю, чтобы исправить его (я делаю хорошие успехи. Я избавился от класса домашних строк и многих контейнеров, выращенных дома).

ОБНОВЛЕНИЕ: чтобы было ясно, я исправил ошибку (изменив «throw e» на «throw») еще до того, как задал вопрос. Я просто искал подтверждение того, что происходит.

ОК, так что просто нормальные преимущества. Я следил за тем, чтобы не было чего-то тонкого и специфичного для исключений, о которых я не знал. Спасибо! Michael Kohne
В чем преимущество ловли как const? Я всегда ловлю по ссылке, но почему const? (не то, чтобы я мог сделать это с этим классом на данный момент в любом случае, но однажды ...) Michael Kohne
Я знаю, что вы сказали не беспокоиться о других вещах, а только о двух других вещах:Exception класс наследуется отstd::exception и ловить вещиconst &. GManNickG
@Kohne: Преимущество const в этом параметре аналогично любому другому параметру: вы четко указываете намерение, которое вы не намереваетесь изменять состояние объекта, и что компилятор должен гарантировать, что вы не - идея позади того, что неизменяемые объекты легче рассуждать (т.е. отлаживать, тестировать, проверять правильность кода), поскольку они помогают в написании ссылочно-прозрачного кода Faisal Vali

Ваш Ответ

4   ответа
21

вы фактически выбрасываете копию объекта, а не оригинал. Подумайте об этом - исходный объект находится в стеке, но стек разматывается и становится недействительным.

Я считаю, что это является частью стандарта, но у меня нет копии для ссылки.

Тип исключения, создаваемого в блоке catch, является базовым типом catch, а не типом брошенного объекта. Обойти эту проблему можноthrow; скорее, чемthrow e; который бросит оригинальное пойманное исключение.

Это будет означать, что я не могу выбросить то, что не имеет конструктора копирования, тогда? В этом есть смысл. Michael Kohne
7

Да.

throw e;

бросает исключениеstatic типeнезависимо от того, чтоe на самом деле В этом случаеDerived исключение копируется вException используя конструктор копирования.

В этом случае вы можете просто

throw;

чтобы получитьDerived Исключение всплывают правильно.

Если вам интересны полиморфные броски в некоторых других случаях, обратитесь квсегда так полезно C ++ FAQ Lite.

10

Быстрый Google предполагает, что да, вы бросаете конструктор копирования, который является обязательным и должен быть общедоступным. (Это имеет смысл, поскольку вы инициализируете копиюe и бросать это.)

В любом случае, просто используйтеthrow без указания объекта исключения, чтобы отбросить то, что было поймано вcatch, Разве это не решит проблему?

  catch(Exception& e)
  {
    printf("Exception seen here! %s %d\n", __FILE__, __LINE__);
    throw;
  }
Я извиняюсь, да, я уже исправил эту ошибку, выполнив правильную переброску. Я пытался получить подтверждение о конструкторе копирования. Michael Kohne
1

если бы это было пари на поведение!

Объект исключения сначала копируется во временный, и вы должны были использоватьthrow, Чтобы процитировать стандарт 15.1 / 3:

A throw-expression initializes a temporary object, called the exception object, the type of which is determined by removing any top-level cv-qualifiers from the static type of the operand of throw and adjusting the type from "array of T" or "function returning T" to "pointer to T" or "pointer to function returning T",respectively.

Я думаю, что это приводит к очень полезному стандартному правилу кодирования:

Base classes of an exception hierarchy should have a pure virtual destructor.

или же

The copy constructor for a base class in an exception hierarchy shall be protected.

Любой из них достигает цели, которую компилятор будет предупреждать, когда вы попытаетесь «выбросить». поскольку в первом случае вы не можете создать экземпляр абстрактного класса, а во втором - потому что вы не можете вызвать конструктор копирования.

Похожие вопросы