Здравствуйте!
При удалении памяти с помощью оператора delete, которая была выделена с помощью оператора new под экземпляр класса, программа падает.
Все указатели хранятся в CArray<type, type>. Падает не стабильно...а при разных условиях использования программы. Причем до падения
все циклы удаления элементов успешно выполняются.
Подскажите пожалуйста, в чем может быть проблема и как поймать ее причину...
Спасибо!
1. А если GetCount() вернет 0 ?
2. А если GetAt(k) возвращает "левый" адрес ?
3. А если RemoveAt(k) удаляет что-то не то ?
Короче, информации в твоем коде — ноль. Поскольку ты не приводишь код, где ты память выделяешь и что ты там размещаешь.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, kupaloff, Вы писали:
K>При удалении памяти с помощью оператора delete, которая была выделена с помощью оператора new под экземпляр класса, программа падает.
pWebBrowser2 — это указатель на COM-объект, реализующий IWebBrowser2 ?
Если да, то его нельзя удалять через delete, нужно использовать стандартный метод IUnknown::Release.
Здравствуйте, LaptevVV, Вы писали:
LVV>1. А если GetCount() вернет 0 ? LVV>2. А если GetAt(k) возвращает "левый" адрес ? LVV>3. А если RemoveAt(k) удаляет что-то не то ? LVV>Короче, информации в твоем коде — ноль. Поскольку ты не приводишь код, где ты память выделяешь и что ты там размещаешь.
1. если GetCount() вернет 0 — то цикл не начнется
2. если GetAt(k) возвращает "левый" адрес — ВОТ! Это как это отследить...
3. если RemoveAt(k) удаляет что-то не то — что значит не то, удаляется k элемент из массива
Здравствуйте, okman, Вы писали:
O>Здравствуйте, kupaloff, Вы писали:
K>>При удалении памяти с помощью оператора delete, которая была выделена с помощью оператора new под экземпляр класса, программа падает.
O>pWebBrowser2 — это указатель на COM-объект, реализующий IWebBrowser2 ? O>Если да, то его нельзя удалять через delete, нужно использовать стандартный метод IUnknown::Release.
нет, но это указатель на класс, наследуемый от CCmdTarget, который подключается к событиям окна IE и перехватывает сообщения от него
Здравствуйте, kupaloff, Вы писали:
K>нет, но это указатель на класс, наследуемый от CCmdTarget, который подключается к событиям окна IE и перехватывает сообщения от него
Здравствуйте, okman, Вы писали:
O>Здравствуйте, kupaloff, Вы писали:
K>>нет, но это указатель на класс, наследуемый от CCmdTarget, который подключается к событиям окна IE и перехватывает сообщения от него
O>А можете привести объявление данного класса ?
Здравствуйте, kupaloff, Вы писали:
K>Здравствуйте, LaptevVV, Вы писали:
LVV>>1. А если GetCount() вернет 0 ? LVV>>2. А если GetAt(k) возвращает "левый" адрес ? LVV>>3. А если RemoveAt(k) удаляет что-то не то ? LVV>>Короче, информации в твоем коде — ноль. Поскольку ты не приводишь код, где ты память выделяешь и что ты там размещаешь.
K>1. если GetCount() вернет 0 — то цикл не начнется
А если GetCount вернет самое большое целое? Тим iCnt неизвестен. Он знаковый или беззнаковый? K>2. если GetAt(k) возвращает "левый" адрес — ВОТ! Это как это отследить...
Надо знать, как выделяется. А то ты закладываешься на проверку NULL, а new() — то может вернуть исключение bad_alloc K>3. если RemoveAt(k) удаляет что-то не то — что значит не то, удаляется k элемент из массива
А как он удаляется?
Не проще ли вместо динамических массивов просто vector использовать? K>вот код выделения памяти:
K>
K>CWebBrowser2* — это класс
1. С одной стороны — ловишь исключение CMemoryException, с другой стороны — проверяешь на NULL.
2. А что делает метод Add ? Опять же, не проще ли vector использовать?
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, kupaloff, Вы писали: K>Здравствуйте, __kot2, Вы писали: __>>это самый дурацко оформленный код, который я когда либо видел за свою жизнь K>И в чем же выражается самый дурацко оформленный код?
K>
K>iCnt = m_oAdvisedWebBrowser2.GetCount(); - венгерская нотация в 2013ом году? она всегда была идиотской, но еще в 90ых даже самые упертые от нее отказались.
почему cnt а не count? может это cunt? зачем надо было вычислять icnt?
webbrowser2 - офигительное название. 2 это что?
GetCount() - это что за контейнер такой? самописный? зачем? почему паскалевское именование ф-ий? это ж С++?
K>for(int k = iCnt - 1; k >= 0; k--) // почему цикл задом наперед? чем это вызвано? чтобы cnt постоянно не считать? так его вычисление и так вынесено наружу. почему k ? что это за протест против кошерного i ?
K>{
K> CWebBrowser2* pWebBrowser2 = m_oAdvisedWebBrowser2.GetAt(k); // хотя бы ссылку, чтоли возвращали вместо указателя. а имена-то какие красивые: с двоечкой в два раза красивее и С торчит впереди имени класса, еще и звезда криво стоит. летопа прямо
K> if(NULL != pWebBrowser2) // хоть бы пробелы, чтоли ставили if ( ...
K> {
K> if(pWebBrowser2->m_bDelete == TRUE) // какое нафиг TRUE в С++? что за бред вообще заводить флаг удаления прямо в браузер? не надо смешивать классы
K> {
K> delete pWebBrowser2; // ниажидана
K> m_oAdvisedWebBrowser2.RemoveAt(k); // общепринято, что контейнеры тут сами вообще-то все удаляют
K> }
K> else{} // что тут имелось в виду? продолжение следует?
K> }
K> else
K> {
K> m_oAdvisedWebBrowser2.RemoveAt(k); // что это за копипаста? мысль нельзя было яснее выразить, когда удаляем, а когда нет?
K> }
K>} где тут вообще С++ ? это какой-то ядреный самопал
K>
Здравствуйте, __kot2, Вы писали:
K>> венгерская нотация в 2013ом году? она всегда была идиотской, но еще в 90ых даже самые упертые от нее отказались.
Смелое заявление
__>GetCount() — это что за контейнер такой? самописный? зачем? почему паскалевское именование ф-ий? это ж С++?
Знакомься, CAtlArray
K>>} где тут вообще С++ ? это какой-то ядреный самопал
Если заменить удаление элементов по одному на RemoveAll после цикла и лишний else (судя по всему там был какой-то код, который _не_ нужен для примера), то это вполне нормальное оформление программы, написанной для Win + MFC.
Здравствуйте, LaptevVV, Вы писали:
K>>1. если GetCount() вернет 0 — то цикл не начнется LVV>А если GetCount вернет самое большое целое? Тим iCnt неизвестен. Он знаковый или беззнаковый?
Во-первых, если GetCount() вернёт что-то слишком большое, это уже свидетельствует о поломке в программе. Откуда там контейнер с несколькими миллиардами элементов?
Во-вторых, из венгерской нотации следует, что iCnt — это int.
В-третьих, если он даже unsigned и >INT_MAX, то ничего страшного: int i=iCnt-1, целочисленное переполнение, получим отрицательное число, не попадём в цикл.
K>>2. если GetAt(k) возвращает "левый" адрес — ВОТ! Это как это отследить... LVV>Надо знать, как выделяется. А то ты закладываешься на проверку NULL, а new() — то может вернуть исключение bad_alloc
Если возникает bad_alloc, то элемент не попадёт в контейнер
K>>3. если RemoveAt(k) удаляет что-то не то — что значит не то, удаляется k элемент из массива LVV>А как он удаляется? LVV>Не проще ли вместо динамических массивов просто vector использовать?
В ATL/MFC программе очень естественно использовать CArray, а не std::vector. Разница между ними невелика.
LVV>1. С одной стороны — ловишь исключение CMemoryException, с другой стороны — проверяешь на NULL.
Вот это действительно перестраховка, память о компиляторах VC5- (до стандарта 98), где new был небросающим.
LVV>2. А что делает метод Add ? Опять же, не проще ли vector использовать?
Интересно, а что мог бы делать метод Add у MFC'шного контейнера CArray?
... и заодно проследить, чтобы нигде не остались голые указатели на CWebBrowser2. (А заполучить голый указатель — как нефиг делать, это же библиотечный класс, а не наш самописный полностью подконтрольный).
Если проблема с кривым владением существует, то она как раз состоит в том, что кто-то владеет этими объектами, помимо контейнера. Ну и какая разница, будем мы вручную реализовывать монопольное владение объектов контейнером, или автоматизируем монопольное владение с помощью shared_ptr?
Хотя, если где-то есть явное воровство указателей непосредственно из контейнера, то shared_ptr поможет найти это место, сломав компиляцию.
Здравствуйте, kupaloff, Вы писали:
K>При удалении памяти с помощью оператора delete, которая была выделена с помощью оператора new под экземпляр класса, программа падает. K>Все указатели хранятся в CArray<type, type>. Падает не стабильно...а при разных условиях использования программы. Причем до падения K>все циклы удаления элементов успешно выполняются.
Возможные причины падения:
1) объект прямо в данный момент кем-то используется, — например, там выполняется асинхронный обмен по http (браузер же)
2) реентер в функцию, в которой выполняется очистка массива
Реентер может быть многопоточным или однопоточным. Однопоточный реентер возникает следующим образом:
— вошли в функцию
— начали удалять первый попавшийся объект
— из его деструктора снова вошли в эту функцию
— снова начали удалять объект...
От многопоточных реентеров спасают мьютексы CCriticalSection.
От однопоточных — двухфазное удаление:
CAdvisedWebBrowser2& arr = m_oAdvisedWebBrowser2; // боже, какие ужасные идентификаторы типа и переменной...
CAdvisedWebBrowser2 arr_to_delete;
int const n = arr.GetCount();
// первая фаза: переносим удаляемые объекты во временный массивfor(int i=0; i!=n; ++i)
{
CAdvisedWebBrowser2*& obj = arr[i]; // ссылка на элемент массиваif(obj && obj->m_bDelete) { arr_to_delete.Add(obj); obj = NULL; } // обнулим этот элемент
}
// вторая фаза: сжимаем исходный массив (повода для реентера ещё нет...)for(int i=n-1; i>=0; --i)
if(!arr[i]) arr.RemoveAt(i);
// третья фаза: исходный массив уже свободен и может быть переиспользованfor(int i=0, nd=arr_to_delete.GetCount(); i!=nd; ++i)
delete arr_to_delete[i];
От реентера это не избавит, но сделает его безопасным.
Однако, если в программе существуют реентеры, — это значит, что в ней есть макароны потоков исполнения. И от них нужно избавляться, тщательно осмысливая, что в какой момент ДОЛЖНО происходить по замыслу, а не "должно бы происходить в имеющемся коде".
Отдавать уничтожение CCmdTarget'ов на откуп деструкторам (их самих или их владельцев) самонадеянно. Там и виндоуз выполняет обряд умерщвления, и MFC к нему прикладывается... в общем, я сейчас не готов развернуть тему, что делать и кого виновать (для этого мне надо ставить винды и студию, и вспоминать былое).
Да вопросы были скорее риторические — привычка студентам задавать, чтобы думали сначала...
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[2]: Удаление динамически выделенной памяти
От:
Аноним
Дата:
04.05.13 17:47
Оценка:
Здравствуйте, Кодт, Вы писали:
К>Однако, если в программе существуют реентеры, — это значит, что в ней есть макароны потоков исполнения. И от них нужно избавляться, тщательно осмысливая, что в какой момент ДОЛЖНО происходить по замыслу, а не "должно бы происходить в имеющемся коде". К>Отдавать уничтожение CCmdTarget'ов на откуп деструкторам (их самих или их владельцев) самонадеянно. Там и виндоуз выполняет обряд умерщвления, и MFC к нему прикладывается... в общем, я сейчас не готов развернуть тему, что делать и кого виновать (для этого мне надо ставить винды и студию, и вспоминать былое).
Проблема более-менее определилась, после логирования и использования SetUnhandledExceptionFilter. Каким-то образом самопроизвольно вызывается деструктор объекта (причем 2 раза), но указатель на объект не удаляется из контейнера. Каждый вызов деструктора логировался с указанием значения указателя объекта вызывавшего его. И когда в программе мы доходим до момента его удаления, согласно логике работы программы...то происходит ТРЕТИЙ вызов деструктора уже удаленного объекта, на что мы получаем вот такую вот штуку:
23:12:53 ae8 ****************************************************
12:14:04 5e4 *** A Programm Fault occured:
12:14:04 5e4 *** Error code C0000005: EXCEPTION_ACCESS_VIOLATION
12:14:04 5e4 ****************************************************
Удаляется память под объекты только в единственном месте, в одной функции, реентер которой регулируется мьютексом, т.е. запустить ее второй раз не удастся ни в другом потоке, ни в этом же, пока она не закончит работу и не освободит мьютекс.
Объекты данного класс присоединяется к событиям открытых окон IE через DIID_DWebBrowserEvents2, соответственно сколько окон(вкладок) столько и объектов. Удаление объекта по логике работы программы происходит после анализа открытых окон IE и сравнения их с хранимыми в контейнере, соответственно если окна нет, а в контейнере оно есть, то удаляется память и удаляется элемент контейнера.
Возникают следующие вопросы:
1) почему деструктор вызывается дважды
2) каким образом и кем деструктор может вызываться, если он точно вызывается не кодом, так как это делается потом
3) "Отдавать уничтожение CCmdTarget'ов на откуп деструкторам (их самих или их владельцев) самонадеянно. Там и виндоуз выполняет обряд умерщвления, и MFC к нему прикладывается... в общем, я сейчас не готов развернуть тему, что делать и кого виновать (для этого мне надо ставить винды и студию, и вспоминать былое)." Поясните пожалуйста это более подробно, может надо совершить какие-то действия или...что?
Часть лога программы где видно вызов деструкторов (неожиданно) и удаление согласно логике работы программы ( где и ожидается удаление):
12:13:49 5e4 OnIETimer
12:13:49 5e4 OnIETimer Mutex created
12:13:49 5e4 OnIETimer Get openning IE windows
12:13:50 128c send_data_thread DELAY = 24 send_data_stop = 0
12:13:50 5e4 OnIETimer Add new web browser
12:13:50 5e4 OnIETimer Add new web browser if 12:13:50 5e4 EA5BE0 OnIETimer new CWebBrowser2 — выделена память и создан объект класса CWebBrowser2
12:13:50 5e4 OnIETimer Add new web browser SetIdispatchPointer
12:13:50 5e4 OnIETimer Add new web browser setHWND
12:13:50 5e4 OnIETimer Add new web browser Attach
12:13:50 1228 SetFlag
12:13:50 1228 SendData Return
12:13:50 1228 send_data_thread_2 return
12:13:50 5e4 OnIETimer Add new web browser SendData
12:13:50 5e4 EA5BE0 URL == NULL
12:13:50 5e4 EA5BE0 pWebBrowser != NULL
12:13:50 5e4 EA5BE0 URL_1 == NULL 12:13:50 5e4 OnIETimer Add new web browser Add — добавление объекта в контейнер
12:13:50 5e4 OnIETimer Add new web browser ok
12:13:50 5e4 OnIETimer Delete cycle start
12:13:50 5e4 OnIETimer m_oAdvisedWebBrowser2 count is 3
12:13:50 5e4 OnIETimer m_oAdvisedWebBrowser2 return 2 element
12:13:50 5e4 OnIETimer NOT Delete monitoring IE window
12:13:50 5e4 OnIETimer m_oAdvisedWebBrowser2 return 1 element
12:13:50 5e4 OnIETimer NOT Delete monitoring IE window
12:13:50 5e4 OnIETimer m_oAdvisedWebBrowser2 return 0 element
12:13:50 5e4 OnIETimer NOT Delete monitoring IE window
12:13:50 5e4 OnIETimer Left 3 monitoring IE windows
12:13:50 5e4 OnIETimer Mutex closed
12:13:50 5e4 OnIETimer Return 12:13:50 5e4 EA5BE0 Checking interface pointer — 1 вызов деструктора 12:13:50 5e4 EA5BE0 Start Reset function — работа по условию if при 1 вызове деструктора (в функции самого деструктора) 12:13:50 5e4 EA5BE0 Checking interface pointer — 2 вызов деструктора (условие if уже не сработало)
12:13:51 128c send_data_thread_delay_175000
12:13:51 128c ServiceDelaySetTimer
12:13:51 128c send_data_thread Mutex closed
12:14:04 5e4 OnIETimer
12:14:04 5e4 OnIETimer Mutex created
12:14:04 5e4 OnIETimer Get openning IE windows 12:14:04 5e4 OnIETimer Delete cycle start — старт цикла удаления невалидный объектов (данных окон IE уже нет)
12:14:04 5e4 OnIETimer m_oAdvisedWebBrowser2 count is 3
12:14:04 5e4 OnIETimer m_oAdvisedWebBrowser2 return 2 element 12:14:04 5e4 EA5BE0 OnIETimer We going to delete memory — попытка вызвать delete pWebBrowser2, 3 вызов деструктора 12:14:04 5e4 EXCEPTION_ACCESS_VIOLATION — сработало Unhandled Exception
12:14:04 5e4 ****************************************************
12:14:04 5e4 *** A Programm Fault occured:
12:14:04 5e4 *** Error code C0000005: EXCEPTION_ACCESS_VIOLATION
12:14:04 5e4 ****************************************************
12:14:04 5e4 *** Address: 00EA5BE0
12:14:04 5e4 *** Flags: 00000000