Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[13.58.197.26] |
|
Страницы: (3) 1 [2] 3 все ( Перейти к последнему сообщению ) |
Сообщ.
#16
,
|
|
|
>Но мне нужно убить его совсем
Видимо, у тебя в голове сложилась не вполне верная картина работы с памяться, и ты исходишь из неверных предпосылок. Вот менеджер памяти сдавал комнату Васе, потом Вася выехал, а комната всё существует, и даже мебель стоит по-старому. Если спросить, сколько кастрюль на верхней полке, ответ будет тоже, что и при Васе. Но она вакантна, и когда-либо может быть снова сдана - Пете, который уже и мебель переставит, и кастрюли свалит в духовку. |
Сообщ.
#17
,
|
|
|
Цитата MBo @ Видимо, у тебя в голове сложилась не вполне верная картина работы с памяться, и ты исходишь из неверных предпосылок. Вот менеджер памяти сдавал комнату Васе, потом Вася выехал, а комната всё существует, и даже мебель стоит по-старому. Если спросить, сколько кастрюль на верхней полке, ответ будет тоже, что и при Васе. Но она вакантна, и когда-либо может быть снова сдана - Пете, который уже и мебель переставит, и кастрюли свалит в духовку. Вполне возможно. Я полагал, что менеджер памяти, имея поляну (память), для Васи оградил кусочек на ней. Вася уехал, менеджер ограду снял, теперь туда может поселиться Петя, которому может понадобиться кусочек побольше или поменьше и оградку надо ставить соответствующую, иначе Коля может попасть на Петину жилплощадь. А так получается, что удаленный методом Free экземпляр Вася (например, класса который представляет точку) делает свободным (вакантным) свой кусочек памяти и, хотя отладчик по-прежнему показывает последние значения Васиных полей и с ними можно производить манипуляции, любой Коля может придти на это место по усмотрению менеджера памяти и в отладчике для экземпляра Вася рано или поздно будут выводится любые, даже абсурдные значения, в то время как переменная Коля в отладчике будет показывать адекватные значения, если я правильно понял. Как мне убедиться, что память, которую занимал Вася, действительно вакантна? |
Сообщ.
#18
,
|
|
|
Цитата А так получается, что удаленный методом Free экземпляр Вася (например, класса который представляет точку) делает свободным (вакантным) свой кусочек памяти и, хотя отладчик по-прежнему показывает последние значения Васиных полей и с ними можно производить манипуляции, любой Коля может придти на это место по усмотрению менеджера памяти и в отладчике для экземпляра Вася рано или поздно будут выводится любые, даже абсурдные значения, в то время как переменная Коля в отладчике будет показывать адекватные значения, если я правильно понял Всё верно >Как мне убедиться, что память, которую занимал Вася, действительно вакантна? Пройти отладчиком по шагам вызов Free с включенными Use Debug DCUs и убедиться, что деструктор срабатывает. Если утечка и есть, то она вызвана другими причинами |
Сообщ.
#19
,
|
|
|
Цитата mnj @ Как мне убедиться, что память, которую занимал Вася, действительно вакантна? Полноценный FastMM + опции FullDebugMode, CheckHeapForCorruption. Заполняют освобожденные участки шаблоном $80808080. Либо в деструкторе занулять занимаемое объектом место |
Сообщ.
#20
,
|
|
|
Спасибо всем за ценные советы. Но в процессе решения этой задачи (точнее, отработки одного из дырявых мест) вот такая есть утечка. Полноценный FastMM говорит, что она в процедуре триангуляции:
5CB160 [Unit1.pas][Unit1][TfrmGL.Triangulation$qqrv][156] Вот объявлены типы (избавились от классов, делаем рекордами) //точка Point = ^P; P = record X: glFloat; Y: glFloat; end; //треугольник PTr = ^T; T = record Vertexes: array[0..2] of P; end; Переменные: Points, Triangles: TList; Вот триангуляция: procedure TfrmGL.Triangulation; var I: integer; Tr: PTr; //треугольник R: real; //векторное произведение B: boolean; PP: Point; //точка (вершина) begin Triangles:= TList.Create; //список треугольников I:= 0; while Points.Count > 3 do begin if I+2 = Points.Count then I:= 0;//если конец списка, то начинаем сначала New(Tr); //пустой треугольник PP:= Points.Items[I]; Tr.Vertexes[0]:= PP^; //первая вершина PP:= Points.Items[I+1]; Tr.Vertexes[1]:= PP^;//вторая вершина PP:= Points.Items[I+2]; Tr.Vertexes[2]:= PP^;//третья вершина R:= VectorMultiply(Tr.Vertexes[0],Tr.Vertexes[1],Tr.Vertexes[2]); B:= IsPIn_Vector(Tr.Vertexes[0],Tr.Vertexes[1],Tr.Vertexes[2],Points); //если R(векторное произведение AB и AC) < 0 - левая тройка векторов и //остальные точки не попадают в текущий треугольник, то if (R < 0) and (B = False)then begin Triangles.Add(Tr);//добавляем треугольник в список треугольников Dispose(Point(Points.Items[I+1])); Points.Delete(I+1); //удаляем вторую (в треугольнике) вершину из списка, //остальные автоматом сдвигаются вперед к первой end else //иначе проверяем следущий набор из трех вершин if I+2 < Points.Count-1 then inc(I) //если не конец списка минус 2 точки else //переходим на 1 точку дальше if I+2 = Points.Count-1 then I:= 0; //если стоим за 2 точки до конца списка end; //идем сначала end Удаление: procedure TfrmGL.FormDestroy(Sender: TObject); var I: integer; begin wglMakeCurrent (0, 0); wglDeleteContext(hrc); for I := 0 to Triangles.Count-1 do Dispose(PTr(Triangles.Items[I])); Triangles.Free; for I := 0 to Points.Count-1 do Dispose(Point(Points.Items[I])); Points.Free; end; Вроде все удаляется, но в репорте 5 штук неопознанных (понятно, что из триангуляции). Где ошибка, не могу найти. Полный код в аттаче. Прикреплённый файлTriangles.rar (55,74 Кбайт, скачиваний: 65) |
Сообщ.
#21
,
|
|
|
New(Tr); выполняется на каждом шаге, а занесение созданного треугольника в список - не на каждом.
Занесенные освобождаются, а незанесенные - остаются висеть. |
Сообщ.
#22
,
|
|
|
Цитата MBo @ New(Tr); выполняется на каждом шаге, а занесение созданного треугольника в список - не на каждом. Занесенные освобождаются, а незанесенные - остаются висеть. Спасибо! Сам бы не додумался о таких простых вещах if (R < 0) and (B = False)then begin Triangles.Add(Tr);//добавляем треугольник в список треугольников Dispose(Point(Points.Items[I+1])); Points.Delete(I+1); //удаляем вторую (в треугольнике) вершину из списка, //остальные автоматом сдвигаются вперед к первой end else //иначе проверяем следующий набор из трех вершин begin if I+2 < Points.Count-1 then inc(I) //если не конец списка минус 2 точки else //переходим на 1 точку дальше if I+2 = Points.Count-1 then I:= 0; //если стоим за 2 точки до конца списка Dispose(Tr); //убираем лишний треугольник и идем сначала end; |
Сообщ.
#23
,
|
|
|
mnj
Зачем раньше времени создавать Tr, если в функции VectorMultiply и IsPIn_Vector можно передать непосредственно точки из Points? К тому же записи (record) давно поддерживают методы, в т.ч. и конструкторы (а для старых версий типа D7 можно вместо record использовать тип object). type //треугольник PTr = ^T; T = record Vertexes: array[0..2] of P; constructor Init(const P1,P2,P3: P); end; constructor PTr.Init(const P1,P2,P3: P); begin Vertexes[0]:=P1; Vertexes[1]:=P2; Vertexes[2]:=P3; end; procedure TfrmGL.Triangulation; var ... Tr: PTr; //треугольник P1,P2,P3: Point; //точка (вершина) begin ... P1:= Points.Items[I]; P2:= Points.Items[I+1]; P3:= Points.Items[I+2]; R:= VectorMultiply(P1^,P2^,P3^); B:= IsPIn_Vector(P1^,P2^,P3^,Points); if (R < 0) and (B = False)then begin New(Tr, Init(P1^,P2^,P3^)); //создаем треугольник и инициализируем его вызовом конструктора Triangles.Add(Tr); //добавляем треугольник в список треугольников ... PS: Названиям типов лучше давать нормальные осмысленные имена, начинающиеся с T..., а не просто P и T как у тебя. |
Сообщ.
#24
,
|
|
|
leo
Цитата leo @ Зачем раньше времени создавать Tr, если в функции VectorMultiply и IsPIn_Vector можно передать непосредственно точки из Points? Да, верно. Тогда не будет нужды удалять ненужные треугольники, так как они не будут создаваться. Насчет конструкторов в записях я просто как-то не думал, пользуя от лени класс от tobject. Но если мне, допустим, нужно соорудить габаритный контейнер для листа карты, то удобнее делать класс (я так думаю): PMyPoint = ^TMyPoint; TMyPoint = record Longitude: real; Latitude: real; constructor Init(const X,Y: real); end; constructor TMyPoint.Init(const X, Y: real); begin Longitude:= X; Latitude:= Y; end; //а в контейнере угловые точки TBorders = class SW: PMyPoint; NW: PMyPoint; NE: PMyPoint; SE: PMyPoint; constructor Create; destructor Destroy; override; end; |
Сообщ.
#25
,
|
|
|
Цитата mnj @ Но если мне, допустим, нужно соорудить габаритный контейнер для листа карты, то удобнее делать класс (я так думаю): А я так не думаю, поскольку TBorders - это фиксированная структура из 4-х точек, которые не имеет смысла выделять динамически по отдельности. Лучше заменить PMyPoint на TMyPoint, тогда и необходимость в деструкторе отпадет (и соотв-но в использовании класса). По моему для габаритной рамки удобнее использовать вариантную запись типа: PMapFrame = ^TMapFrame; TMapFrame = array[0..3] of TMyPoint; PBorders = ^TBorders; TBorders = record case integer of 0: (Frame: TMapFrame); //точки в виде массива 1: (SW, NW, NE, SE: TMyPoint); //отдельные точки с осмыслеными названиями end; В эту запись можно добавить конструктор(ы) для инициализации, а также некоторые методы, например, функцию определения попадания точки в габариты (типа PointInFrame). |
Сообщ.
#26
,
|
|
|
Цитата mnj @ Points, Triangles: TList; В данном случае использовать стандартный TList неудобно, т.к. приходится писать кучу лишнего кода для удаления\освобождения его элементов. Лучше его переопределить, чтобы память под удаляемые элементы освобождалась автоматически. Для этого достаточно переопределить метод TList.Notify TFreeMemList = class(TList) protected procedure Notify(Ptr: Pointer; Action: TListNotification); override; end; procedure TFreeMemList.Notify(Ptr: Pointer; Action: TListNotification); begin if (Ptr <> nil) and (Action = lnDeleted) then FreeMem(Ptr); end; |
Сообщ.
#27
,
|
|
|
Вообще, получается, что в моем случае классы, как строительный материал, не нужны, все можно сконструировать записями, тот же объект на карте:
TMapObject = record //объект на карте Order: byte; //байт для сортировки при записи, означает порядок рисования Class_code: integer; //код классификатора объекта Key: integer; //уникальный номер объекта Local: byte; //код локализации - линейный, площадной, точечный и т.п. Borders: TBorders; //габаритный контейнер NumberOfSubs: integer; //число подобъектов NumberOfPoints: integer; //количество точек NumberOfSem: integer; //количество семантических данных CoordList: array of TMyPoint; //список координат SubObj: array of TMapObject; //список подобъектов Name: ANSIstring; //название или подпись (если есть) Semantic: integer; //семантический ключ для рисования/нерисования Visibility: boolean; //true- рисуется в любом масштабе, false - проверяется на размер constructor Create(AVisibility: boolean); end; Цитата leo @ Такой список можно использовать для любых простых записей, не требующих финализации (т.е. не содержащих строк и т.п.), когда для освобождения элемента можно использовать FreeMem вместо Dispose (=Finalize+FreeMem) А как очистить TList, заполненный такими TMapObject? |
Сообщ.
#28
,
|
|
|
Цитата mnj @ Вообще, получается, что в моем случае классы, как строительный материал, не нужны, все можно сконструировать записями, тот же объект на карте: Не нужно впадать в крайности. Не имеет смысла создавать классы для простых\примитивных структур типа точек, треугольников, габаритов и т.п. А TMapObject - довольно навороченная структура, которая к тому же может иметь (а может и не иметь) подобъекты SubObj аналогичной структуры, которые имеет смысл создавать динамически. Поэтому, на мой взгляд, лучше объявить TMapObject как класс, заменить тип SubObj на TObjectList и удалять его в деструкторе. Цитата mnj @ А как очистить TList, заполненный такими TMapObject? Если записи не простые (содержат строки, динамические массивы и т.п.), то удалять их нужно через Dispose(Тип_указателя_записи(Ptr)); |
Сообщ.
#29
,
|
|
|
Цитата leo @ Не нужно впадать в крайности. Не имеет смысла создавать классы для простых\примитивных структур типа точек, треугольников, габаритов и т.п. Стараюсь . Но в тех же габаритах есть необходимость учитывать формат координат (градусы или метры) и производить манипуляции в зависимости от текущего формата. Получается, что нужен все-таки класс, чтобы воспользоваться полиморфизмом и определить метод TBorders.DoSomething для градусной нотации и для метрической. |
Сообщ.
#30
,
|
|
|
Цитата mnj @ Но в тех же габаритах есть необходимость учитывать формат координат (градусы или метры) и производить манипуляции в зависимости от текущего формата. Получается, что нужен все-таки класс... Не знаю. Всё таки TBorders - это не самостоятельная сущность, а просто свойство\характеристика объекта карты или карты в целом. Поэтому учет "DoSomething для градусной нотации и для метрической" может делаться в этих объектах, а не в самом TBorders. Всё зависит от конкретики (в частности от того, что действительно скрывается за DoSomething), иначе можно опять "впасть в крайность" и прийти к выводу, что точка и треугольник тоже должны быть классами, т.к. они также должны учитывать "градусную и метрическую нотацию". |