2016-08-11 5 views
1

Я компилирую библиотеку C++, и у меня есть проблема с деструкторами и noexcept.Исключение броска в деструкторе производного класса

Это мое понимание, что, поскольку C++ 11, значение по умолчанию noexcept(...) внутри деструкторов изменилось. В частности, теперь деструкторы по умолчанию равны noexcept (то есть noexcept(true)).

Теперь у меня есть библиотека, для которой я получаю много предупреждений компилятора из-за того, что деструктор некоторых классов может бросать, но он не помечен noexcept(false). Поэтому я решил изменить это и добавить спецификацию. Однако эти классы унаследованы от некоторого общего родителя Base, поэтому это вынудило его добавить его также в деструктор родительского класса, иначе я получу ошибку компилятора overriding ‘virtual Base::~Base() noexcept.

Конечно, я могу добавить noexcept(false) также в базовый класс (на самом деле его достаточно добавить, насколько я понимаю), но есть много классов, которые наследуют от Base, и все они наследуют атрибут noexcept(false) (насколько я понимаю). Однако сам деструктор базового класса будет никогда не бросить, и только некоторые из производных классов могут реально бросить. Поэтому появляется отходы, чтобы отметить деструктор базового класса noexcept(false) только для нескольких классов.

Итак, у меня есть два вопроса:

1) Есть ли способ обойти это? Я не могу удалить броски в нескольких производных классах, которые бросают, поскольку они важны.

2) Я не уверен, насколько можно оптимизировать оптимизацию компилятора из-за добавления noexcept(false) в базовый класс (практически) всех классов в библиотеке. Когда (когда-либо) это меня касается?

Редактировать: на боковой ноте, есть способ изменить значение по умолчанию для noexcept внутри проекта? Возможно, вариант компилятора?

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

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

2) Когда я сказал: «Я не могу удалить броски в нескольких производных классах, которые бросают, поскольку они важны». Я имел в виду, что я считаю, что код должен заканчиваться в этом случае, поскольку что-то действительно плохое и неопределенное поведение может следовать в любом случае. По крайней мере, бросок приходит с небольшим объяснением того, что произошло.

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

+2

* «... Я не могу удалить« броски »в нескольких производных классах, которые бросают, так как они важны.» * - Похоже на плохой плохой и плохо плохо спроектированной библиотеки. [Throwing from Destructor] (http://stackoverflow.com/questions/130117/throwing-exceptions-out-of-a-destructor?rq=1) является опасным. – WhiZTiM

+1

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

+4

"* Я не могу удалить броски в нескольких производных классах, которые бросают, поскольку они важны. *« Да, вы можете. Вы просто не хотите, чтобы изменения действительно делали это *. Но факт в том, что вы, несомненно, способны это сделать. Вы, возможно, разработали себя в угол, но вы всегда можете снова выстраиваться из него. –

ответ

2

Если деструктор является виртуальным, производные деструкторы не могут быть noexcept(false), если базовым деструктором также является noexcept(false).

Подумайте об этом: точкой виртуальных функций является то, что производный класс может быть вызван, даже если вызывающий абонент знает только о базовом классе.Если что-то вызывает delete на Base*, вызов может быть скомпилирован без кода обработки исключений, если Base обещает, что его деструктор не будет генерировать исключения. Если указатель указывает на экземпляр Derived, деструктор которого фактически делает, выведите исключение, это было бы плохо.

Попробуйте изменить производные деструкторы, чтобы они не выбрасывали исключения. Если это невозможно, добавьте noexcept(false) в базовый деструктор.

+0

Спасибо за разъяснение, почему у вас не должно быть дескриптора 'noexcept (false)' в деструкторе производного класса, если у вас нет его в деструкторе родительского класса. Имеет смысл. – bartgol

2

Я не знаю, сколько оптимизация компилятор может ухудшиться из-за добавления noexcept(false) к базовому классу (практически) всех классов в библиотеке. Когда (когда-либо) это меня касается?

Вы не должны беспокоиться о оптимизации компилятора на основе объявлений noexcept о деструкторах. Большинство оптимизаций вокруг объявлений noexcept исходят из кода, который определяет, что операция noexcept и делает что-то более эффективное на основе этого. То есть, явное метапрограммирование.

Почему не так важно для деструкторов? Ну, рассмотрим стандартную библиотеку C++. Если вы передадите vector тип и один из деструкторов содержащихся объектов, вы получите неопределенное поведение. Это справедливо для всего в стандартной библиотеке (если не указано ).

Это означает, что стандартная библиотека не будет пытаться проверить, является ли ваш деструктор noexcept или нет. Он может, и почти наверняка, предположить, что ни один деструктор не испускает исключение. И если кто-то ... ну, это ваша вина.

Таким образом, ваш код, скорее всего, не будет работать медленнее только потому, что вы используете noexcept(false) на деструкторе. Ваше решение о том, следует ли использовать noexcept(false) в деструкторе базового класса, не должно зависеть от проблем с производительностью.