2009-05-08 3 views
2

У нас есть класс, семантическое поведение как следующее: -Бросающие деструкторы, коррупция памяти?

struct Sample 
{ 
    ~Sample() throw() 
    { 
    throw 0; 
    } 
}; 

void f() 
{ 
    try 
    { 
    delete new Sample; 
    } 
    catch (...){ 
    } 
} 

Я знаю, что бросать исключения в dtors зло; но отказ от ресурса библиотеки сторонних организаций бросает исключение (но может быть немедленно восстановлен, что-то странное!). Существует также пул этого ресурса, например массив/контейнер класса Sample. Итак, рассмотрим два случая: уничтожение динамически выделенного объекта и уничтожение динамически выделенного массива объектов.

В настоящее время приложение аварийно падает в разных точках исполнения только тогда, когда используется версия массива (пул). Мы полагаем, что это связано с повреждением памяти, но почему же работает эта неохлаждаемая версия ?.

Что происходит с выделенной памятью? Это неопределенное поведение? Что происходит в случае массива? Должны ли вызываться dtors (по крайней мере, а не память) всех элементов массива (скажем, если dtor первого элемента бросает)?

Спасибо заранее,

EDIT-1: Ну, мы отслеживали его вниз к dtors некоторых массивов элементов не называется. Но выделенная память, кажется, не имеют проблем ... Ниже раздел 5.3.5.7 из SC22-N-4411.pdf)

If the value of the operand of the delete-expression is not a null pointer value, the delete-expression will 
call a deallocation function (3.7.4.2). Otherwise, it is unspecified whether the deallocation function will be 
called. [ Note: The deallocation function is called regardless of whether the destructor for the object or some 
element of the array throws an exception. —end note ] 

< \ надрез>

Похоже, память всегда освобождается в таких случаях. Правильно ли я интерпретирую стандарт?

ответ

5

Есть две вещи, которые могут произойти в этой ситуации:

  • прекращается() называется
  • неопределенными поведение

В любом случае невозможно гарантировать освобождение динамически распределенной памяти (за исключением того, что завершение приложения, конечно, вернет все res ources к ОС).

4

C++ прекратит ваше приложение, если dtor выбрасывает исключение, когда стек разматывается из-за другого исключения.

Как невозможно определить, при каких обстоятельствах вызывается dtor, стандартное правило равно never исключить исключения из dtors.

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

Сделайте это, а затем посмотрите, работает ли ваша коллекция объектов, это может привести к вашим авариям.

UPDATE

К сожалению, я не спец адвокат, предпочитая Fisherman's Friend подход «сосать ГЭЭ».

Я бы написал небольшое приложение с классом, который выделяет мега-кучу. В цикле создайте массив классов, пусть классы dtor выдают исключение и бросают catch в исключение в конце цикла (заставляя стек разматывать и вызывать dtors массива классов) и наблюдать за ним чтобы увидеть, как ваше использование VM проходит через крышу (что я уверен, что это произойдет).

К сожалению, я не могу дать вам главу и стих, но это моя «вера»

+0

Currenly Я не хочу изменять класс Sample. Я знаю abt std :: terminate() и т. Д. Имея спецификацию throw(). для dtor также бесполезно, поскольку это не помешает любым исключениям избежать или не допустить, чтобы кто-то бросал. Мой вопрос более склонен к тому, что происходит с памятью? Выпущена ли она? Протекает ли он? Или это Неопределенное поведение? Цитирование стандартных разделов C++ поможет. – Abhay

+0

O.K. Для меня тоже стандарт C++. Единственная причина, по которой я спрашиваю; если я хочу запретить исключение/исключение из dtors в моем текущем проекте, мне понадобится помощь «spec lawyer's» :-) – Abhay

1

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

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

Стандартный способ написания деструктора должно быть что-то вроде:

A::~A() 
{ 
    try { 
     // some cleanup code 
    } 
    catch (...) {} // Too bad we will never know something went wrong but application will not crash 
} 
+0

1) Хорошо. Хорошо известно. Но я не намерен изменять класс Sample. 2) Не могли бы вы процитировать стандартные разделы C++, посвященные удалению массива, когда исключение вызывается dtor одного из его элементов. Спасибо – Abhay

1

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

T* p = new T[10]; 
delete[] p; 

Итак, как будет новый [] и удалите [] реагировать, если T бросает деструктор?

Давайте сначала рассмотрим, что все конструкции прошли гладко, а затем во время удаления [] четвертый или так деструктор выбрасывает. delete [] может выбрать распространение исключения, которое приведет ко всем остальным T-объектам, оставшимся в массиве, потерянным, не извлекаемым и, следовательно, незавершенным. Он также не может «поймать» исключение, потому что тогда delete больше не будет исключением.

Во-вторых, скажите, что один из конструкторов выбрасывает. Скажем, 6-й конструктор выдает исключение. Во время разворачивания штанов все объекты, которые были построены до сих пор, должны быть деконструированы. Так вызывается вызов 5-го, 4-го, 3-го и так далее деструкторов. Что произойдет, если 4-й или 3-й деструктор выбрасывает другое исключение? Должно ли оно быть абсо- лютным или размножаемым?

На это нет ответа, поэтому этот вопрос приводит к неопределенному поведению.

И, как указано в моем первом примере, также может привести к утечкам памяти ..

+1

«Что произойдет, если 4-й или 3-й деструктор выбрасывает другое исключение?» terminate() называется (15.5.1). –

2

Поскольку вы просили в комментарии для главы и стихи:

15,2: 3 имеет примечание, говоря:

«Если деструктор вызывается во время стека размотки выходов с исключением прекратить называется (15,5. 1) .Таким образом, деструкторы обычно должны улавливать исключения и не допускать их распространения из деструктора. «

Насколько я могу судить, единственным оправданием для высказывания« вообще »там является то, что можно очень тщательно написать чтобы ни один объект, деструктор которого не может бросить, когда-либо удаляется как часть разворачивания стека. Но это сложнее условие для выполнения в среднем проекте, чем «деструкторы не должны бросать».

15.5.1 и 2 говорят:

«В следующих ситуациях ... - когда разрушение объекта во время раскручивания стеки (15.2), выезды с использованием исключения ... void terminate() называется».

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

Похоже, что память всегда снимается в таких случаях. Правильно ли я интерпретирую стандарт?

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

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

+0

Спасибо или ваши входы. Класс Sample - это оболочка для класса библиотеки thrid-party, которая сейчас замораживается. Поэтому мы завернули его снова, когда наш dtor гарантирует, что исключения не исчезнут. Но суть проблемы была; некоторые dtors массива не вызывались из-за исключения из dtor более ранних элементов. Infact я обнаружил из C++ Std. что массивы dtors называются последним-первым (уменьшая память) по-разному. Но объект, которому принадлежит этот массив, был освобожден, несмотря на исключение из одного из его элементов массива. Это приводит к серьезному повреждению памяти и случайным сбоям. – Abhay