2014-10-13 2 views
4

Я пытаюсь написать модульный тест, который обнаруживает недопустимое использование функции lock() моего класса. Для этого я хочу использовать деструктор и исключить из него исключение. К сожалению, вместо исключения исключения g ++ решает вызвать std :: terminate().Почему я всегда получаю «завершение вызова после вызова экземпляра ...» при метании моего деструктора?

Существует очень упрощенная версия класса:

class A 
{ 
public: 
    A() : f_lock(0) {} 
    ~A() { if(f_lock) throw my_exception("still locked"); } 
    lock() { ++f_lock; } 
    unlock() { --f_lock; } 
private: 
    int f_lock; 
}; 

Существует действительное испытание:

A *a = new A; 
a->lock(); 
... 
a->unlock(); 
delete a; 

Существует инвалиду тест, который я пытаюсь написать:

A *a = new A; 
a->lock(); 
... 
bool success = false; 
try 
{ 
    delete a; 
} 
catch(my_exception const&) 
{ 
    success = true; 
} 
catch(...) 
{ 
    // anything else is a failure 
} 
if(!success) 
{ 
    // test failed... 
    CPPUNIT_ASSERT(!"test failed"); 
} 

Прямо сейчас, delete звонит std::terminate(), хотя бросок не указывается d, когда другое исключение активно. (то есть std::uncaught_exception() является ложным.) И также я, очевидно, поймаю все исключения!

Я делаю что-то неправильно или g++ запрограммирован так, чтобы это всегда было в деструкторах?


Update:

Ответ на DYP в комментариях ниже работ! Далее непосредственно не называют std::terminate():

~A() noexcept(false) { throw ...; } 

Также для справки о том, почему вы не хотите, заброс в деструкторе, эта страница отлично;

https://www.securecoding.cert.org/confluence/display/cplusplus/ERR33-CPP.+Destructors+must+not+throw+exceptions


Для выяснения, есть полная версия деструктора. Как мы видим, я сначала отправляю сообщение (оно обычно входит в вашу консоль, может также идти в журнал). Во-вторых, я уверен, что мы еще не управляем исключением. Наконец, я выбрал исключение exception_exit, которое, как ожидается, заставит terminate(), хотя в графическом приложении вам может понадобиться показать сообщение MessageBox, чтобы пользователь знал что-то (так как вы можете захватить сообщение, вы можете отображать это пользователю), а затем принудительно отключить приложение.

Node::~Node() noexcept(false) 
{ 
    if(f_lock > 0) 
    { 
     // Argh! A throw in a destructor... Yet this is a fatal 
     // error and it should never ever happen except in our 
     // unit tests to verify that it does catch such a bug 
     Message msg(message_level_t::MESSAGE_LEVEL_FATAL, err_code_t::AS_ERR_NOT_ALLOWED); 
     msg << "a node got deleted while still locked."; 

     // for security reasons, we do not try to throw another 
     // exception if the system is already trying to process 
     // an existing exception 
     if(std::uncaught_exception()) 
     { 
      // still we cannot continue... 
      std::abort(); 
     } 

     throw exception_exit(1, "a node got deleted while still locked."); 
    } 
} 

Кроме того, еще одна деталь, вы должны использовать NodeLock объект для управления флагом F_LOCK. Это безопасно для исключений, поскольку использует RAII (то есть блокировку с привязкой). Однако в этот момент я не хотел принуждать пользователя использовать NodeLock для блокировки/разблокировки узла, следовательно, этот тест в деструкторе.

+2

Начиная с C++ 11, большинство деструкторов по умолчанию являются 'noexcept'.Попробуйте объявить его как «noexcept (false)» (или, еще лучше, попытайтесь создать свои классы, чтобы не выбрасывать деструктор). – dyp

+1

Мне очень любопытно, чего вы ожидаете. Скажем, вы поймаете исключение - у вас есть объект, который выходит за пределы области действия, но не разрушен. Чем ты занимаешься? –

+0

@ dyp, спасибо за ответ! Это сработало. Извините, я не могу дать вам баллы за это, так как Matt & Jonathan решил, что это дубликат аргумента, почему вы не должны делать то, что я пытаюсь сделать, но НИКАКОЙ ОТВЕТЬТЕ на мой вопрос. Очень раздражает, если вы спросите меня. –

ответ

3

В C++ 11 добавлено ключевое слово noexcept. Это может быть использовано в спецификации исключений функции:

  • noexcept(true) такое же, как throw(), т.е.эта функция terminate s если что-то брошено
  • noexcept(false) означает, что функция может бросить что-нибудь

Для большинства функций, они не имеют исключение спецификации, если не дать им один. Функция без исключение-спецификация может выбросить что-либо.

Существует особый случай для деструкторов, хотя, находится в C++ 11 [class.dtor]/3:

Объявления деструктора, который не имеет исключение спецификации неявно считается, что те же исключение - спецификация как неявное объявление (15.4).

по ссылке правило, 15,4, говорит, что неявно объявленные функции специального члена всегда имеет исключение спецификации. Спецификация определяется по следующему правилу, [except.spec]/14:

неявно объявляется специальная функция-члена (пункт 12) должны иметь исключение спецификации. Если f является неявным объявленным конструктором по умолчанию, конструктором копирования, конструктором перемещения, деструктором, оператором присваивания копий или оператором присваивания переадресации, его неявная спецификация исключения указывает идентификатор типа T тогда и только тогда, когда исключение -спецификация функции, непосредственно вызванной неявным определением f; f разрешает все исключения, если любая функция, которую он вызывает непосредственно, разрешает все исключения, а f не допускает исключений, если каждая функция, которую он вызывает напрямую, не допускает исключений.

«Функция, которую он непосредственно вызывает» в этом разделе означает деструктор любой переменной-члена или базового класса (применяется рекурсивно). Если такой функции нет, то таких исключений не допускается, поэтому по умолчанию это noexcept(true).

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

  • Если бы все подобъекты либо не имеет деструктора, или неявно сгенерированный деструктора или деструктора объявлена ​​как noexcept(true) или эквивалент; то деструктор этого класса по умолчанию равен noexcept(true).

Итак, изменение деструктора на noexcept(false) приведет к воспроизведению поведения C++ 03.


В C++ 03 ничего из этого не появилось, и ваш деструктор отказался бы от всех исключений. Я не знаю точно, почему это изменение было внесено в C++ 11, но может быть, потому что это плохая идея выбраться из деструктора, если вы действительно не знаете, что делаете.

+0

Связано это: http://stackoverflow.com/questions/16540725/c11-exceptions-destructor-allows-to-throw-now –

+0

Я думаю, что лучше по умолчанию не разрешать бросок по умолчанию, за исключением того, что компилятор должен сказать вы так во время компиляции, а не просто вызываете std :: terminate, а не бросаете. Возможно, есть предупреждение для этого случая ... В противном случае причиной, по которой мне нравится иметь исключение, является возможность проверить потенциальную ошибку в моем модульном тесте. Если система просто заканчивается, я не могу написать простой единичный тест (запись двоичного кода на анонимное завершение не является доказательством того, что тест прошел успешно). В моем коде исключение называется 'exception_exit', и когда вы поймаете это исключение, вы должны выйти , –

Смежные вопросы