Вопрос по exception, c++, visual-c++ – C ++: создание исключения вызывает конструктор копирования?

1

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

class AFX_CLASS_EXPORT CCLAError : public CObject

Он имеет следующий конструктор копирования:

CCLAError(const CCLAError& src) { AssignCopy(&src); } // (AssignCopy is a custom function)

Первоначально он был написан и скомпилирован / связан с MSVC6 (Visual Studio 2003). Я нахожусь в процессе внесения необходимых изменений, чтобы заставить его скомпилировать и связать с MSVC8 + (VS 2008+)

Когда вызывается компоновщик msvc8, я получаю следующую ошибку:

LNK2001: unresolved external symbol "private: __thiscall CObject::CObject(class CObject const &)" ([email protected]@[email protected]@@Z)

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

Я впервые увидел ошибку при компиляции библиотеки, которая определяет и сначала выдаетCCLAErrorВот почему я продолжаю, как будто это причина.

Мне удалось устранить ошибку, изменив

throw CCLAError( ... )

в

throw new CCLAError( ... )

а также

catch(CCLAError& e)
{
   throw e;
}

в

catch(CCLAError& e)
{
   throw;
}

Однако я не понимаю, почему перезапуск перехваченного исключения вызовет конструктор копирования. Я что-то упускаю совершенно очевидно? Впоследствии, почему удаление переменной, содержащей ссылку на перехваченное исключение, приведет к тому, что конструктор копирования не будет вызван?

Вы не должны и не должны использоватьnew при броске оригинального исключения. Но вам определенно нужно изменитьthrow e чтобы простоthrow перебросить существующее исключение, не создавая новый экземпляр этого. Remy Lebeau

Ваш Ответ

6   ответов
0

вы неправильно поняли, что означает повторный бросок. Когда вы делаете -

catch(CCLAError& e)
{
   throw e;
}

- вы НЕ бросаете. Вместо этого, как вы заметили, вы действительно создаете копию нового исключения. Вместо этого (опять же, как вы сами убедились), это то, что технически является правильным способом повторного броска:

catch(CCLAError& e)
{
   throw;
}

Прочитайте главу 14 в TC ++ PL Страуструпа (14.3.1 касается повторного бросания).

Кроме того, вам не нужно делать -

throw new CCLAError( ... )

Вместо этого делайте -

throw CCLAError( ... )

- как вы делали раньше, только получать поCONST ссылка (вы не можете держать ссылку на временный).

catch(const CCLAError &e)
throw CCLAError ( ... ) вызывает ошибку компоновщика LNK2001, описанную в OP. johnluetke
Относительно вашего последнего предложения: нет временныхCCLAError& e будет привязан к объекту исключения. Не нужно делать копии. Как вы думаете, где есть временный?
3

1-й из всех не звонитеthrow new foo() использованиеthrow foo

2-й, когда вы пишете:

catch(foo const &e) {
   throw e;
}

Вы фактически создаете новое исключение, и если, например, это исключение был брошен это подклассfoo они мое призваниеthrow e вы бы потеряли это информация и фактически использовать конструктор копирования для генерацииfoo от е - что угодно это было.

Теперь, когда вы звоните

catch(foo const &e) {
   throw;
}

Вы не создаете новое исключение, а скорее распространяете то же исключение.

Так:never использованиеthrow e; чтобы распространить исключение вперед, используйтеthrow;

В соответствии с моим комментарием к ответу К-балла, это старый код (который гласит:throw e) то подтекает? johnluetke
-1

дного объекта, сколько они хотят. Ограничений по количеству копий нет.

Это означает, что ваш пользовательский класс исключений должен иметьaccessible copy-constructor, либо сгенерированный компилятором, либо определяемый пользователем.

9

throw Выражение может создавать копию своего аргумента (копия может быть исключена или в C ++ 11 вместо этого может быть выполнено перемещение, но конструктор копирования все еще должен быть доступен и вызываться).

Возврат исключения с использованиемthrow; не будет создавать никаких копий. Бросать пойманный объект исключения с помощьюthrow e; вызовет копиюe быть произведенным. Это не то же самое, чтоrethrowing исключение.

Ваш & quot; обновленный & quot; код не работает, как вы ожидаете.catch (CCLAError&) не поймает исключение типаCCLAError*, который является типом исключения, выдаваемогоthrow new CCLAError(...);, Вам нужно будет пойматьCCLAError*, Не делай этого, хотя. Бросать исключения по значению и ловить по ссылке. Все типы исключений должны быть копируемыми.

1

объект, который вы выбрасываете, обычно находится в стеке. Стек очищается как часть процесса выброса, поэтому компилятор должен сделать копию, которая может продолжать жить после этой точки.

Когда вы бросаете объект сnewвы не бросаете фактический объект, но вы бросаете указатель на объект. Это означает, что вашcatch Блок должен также поймать указатель, а не ссылку. Не забудьтеdelete указатель или у вас утечка памяти!

Когда блок catch используетthrow; вместоthrow e; он может повторно использовать копию, сделанную ранее, и нет необходимости делать другую копию.

5

I do not understand why re-throwing a caught exception would invoke the copy constructor.

Это не так, но перебрасывание брошенного исключения выполняется сthrow;, Когда вы делаетеthrow e; Вы просите выбросить копию пойманного исключения.

Имеет смысл. посколькуthrow e был действителен в MSVC6, а теперь не в MSVC8 (как продемонстрированоthrow будучи действительными), они оба достигают того же самого результата? Или старый код пропускает память? johnluetke
@johnluetke: всякий раз, когда выthrow указатель затем его утечка (пытаетсяdelete это хлопотно). А такжеthrow e может привести к нарезке, если вы поймете по значению, поэтомуthrow; есть.

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