На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! ПРАВИЛА РАЗДЕЛА · FAQ раздела Delphi · Книги по Delphi
Пожалуйста, выделяйте текст программы тегом [сode=pas] ... [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.
Следующие вопросы задаются очень часто, подробно разобраны в FAQ и, поэтому, будут безжалостно удаляться:
1. Преобразовать переменную типа String в тип PChar (PAnsiChar)
2. Как "свернуть" программу в трей.
3. Как "скрыться" от Ctrl + Alt + Del (заблокировать их и т.п.)
4. Как прочитать список файлов, поддиректорий в директории?
5. Как запустить программу/файл?
... (продолжение следует) ...

Вопросы, подробно описанные во встроенной справочной системе Delphi, не несут полезной тематической нагрузки, поэтому будут удаляться.
Запрещается создавать темы с просьбой выполнить какую-то работу за автора темы. Форум является средством общения и общего поиска решения. Вашу работу за Вас никто выполнять не будет.


Внимание
Попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
Повторная попытка - 60 дней. Последующие попытки бан.
Мат в разделе - бан на три месяца...
Модераторы: jack128, D[u]fa, Shaggy, Rouse_
Страницы: (3) 1 [2] 3  все  ( Перейти к последнему сообщению )  
> Работа с TObjectList , Не освобождается память при удалении элемента-объекта из переменной типа TObjectList
    >Но мне нужно убить его совсем

    Видимо, у тебя в голове сложилась не вполне верная картина работы с памяться, и ты исходишь из неверных предпосылок.

    Вот менеджер памяти сдавал комнату Васе, потом Вася выехал, а комната всё существует, и даже мебель стоит по-старому. Если спросить, сколько кастрюль на верхней полке, ответ будет тоже, что и при Васе.
    Но она вакантна, и когда-либо может быть снова сдана - Пете, который уже и мебель переставит, и кастрюли свалит в духовку.
      Цитата MBo @
      Видимо, у тебя в голове сложилась не вполне верная картина работы с памяться, и ты исходишь из неверных предпосылок.

      Вот менеджер памяти сдавал комнату Васе, потом Вася выехал, а комната всё существует, и даже мебель стоит по-старому. Если спросить, сколько кастрюль на верхней полке, ответ будет тоже, что и при Васе.
      Но она вакантна, и когда-либо может быть снова сдана - Пете, который уже и мебель переставит, и кастрюли свалит в духовку.

      Вполне возможно. Я полагал, что менеджер памяти, имея поляну (память), для Васи оградил кусочек на ней. Вася уехал, менеджер ограду снял, теперь туда может поселиться Петя, которому может понадобиться кусочек побольше или поменьше и оградку надо ставить соответствующую, иначе Коля может попасть на Петину жилплощадь. А так получается, что удаленный методом Free экземпляр Вася (например, класса который представляет точку) делает свободным (вакантным) свой кусочек памяти и, хотя отладчик по-прежнему показывает последние значения Васиных полей и с ними можно производить манипуляции, любой Коля может придти на это место по усмотрению менеджера памяти и в отладчике для экземпляра Вася рано или поздно будут выводится любые, даже абсурдные значения, в то время как переменная Коля в отладчике будет показывать адекватные значения, если я правильно понял. Как мне убедиться, что память, которую занимал Вася, действительно вакантна?
      Сообщение отредактировано: mnj -
        Цитата
        А так получается, что удаленный методом Free экземпляр Вася (например, класса который представляет точку) делает свободным (вакантным) свой кусочек памяти и, хотя отладчик по-прежнему показывает последние значения Васиных полей и с ними можно производить манипуляции, любой Коля может придти на это место по усмотрению менеджера памяти и в отладчике для экземпляра Вася рано или поздно будут выводится любые, даже абсурдные значения, в то время как переменная Коля в отладчике будет показывать адекватные значения, если я правильно понял


        Всё верно

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

        Пройти отладчиком по шагам вызов Free с включенными Use Debug DCUs и убедиться, что деструктор срабатывает. Если утечка и есть, то она вызвана другими причинами
          Цитата mnj @
          Как мне убедиться, что память, которую занимал Вася, действительно вакантна?

          Полноценный FastMM + опции FullDebugMode, CheckHeapForCorruption. Заполняют освобожденные участки шаблоном $80808080. Либо в деструкторе занулять занимаемое объектом место
            Спасибо всем за ценные советы. Но в процессе решения этой задачи (точнее, отработки одного из дырявых мест) вот такая есть утечка. Полноценный FastMM говорит, что она в процедуре триангуляции:
            5CB160 [Unit1.pas][Unit1][TfrmGL.Triangulation$qqrv][156]
            Вот объявлены типы (избавились от классов, делаем рекордами)
            ExpandedWrap disabled
                //точка
                Point = ^P;
                P = record
                  X: glFloat;
                  Y: glFloat;
                end;
                //треугольник
                PTr = ^T;
                T = record
                  Vertexes: array[0..2] of P;
                end;

            Переменные:
            ExpandedWrap disabled
                Points, Triangles: TList;

            Вот триангуляция:
            ExpandedWrap disabled
              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
            ;
            Удаление:
            ExpandedWrap disabled
              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)
              New(Tr); выполняется на каждом шаге, а занесение созданного треугольника в список - не на каждом.
              Занесенные освобождаются, а незанесенные - остаются висеть.
                Цитата MBo @
                New(Tr); выполняется на каждом шаге, а занесение созданного треугольника в список - не на каждом.
                Занесенные освобождаются, а незанесенные - остаются висеть.

                Спасибо! Сам бы не додумался о таких простых вещах
                ExpandedWrap disabled
                       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;
                  mnj
                  Зачем раньше времени создавать Tr, если в функции VectorMultiply и IsPIn_Vector можно передать непосредственно точки из Points?
                  К тому же записи (record) давно поддерживают методы, в т.ч. и конструкторы (а для старых версий типа D7 можно вместо record использовать тип object).
                  ExpandedWrap disabled
                    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 как у тебя.
                    leo
                    Цитата leo @
                    Зачем раньше времени создавать Tr, если в функции VectorMultiply и IsPIn_Vector можно передать непосредственно точки из Points?

                    Да, верно. Тогда не будет нужды удалять ненужные треугольники, так как они не будут создаваться. Насчет конструкторов в записях я просто как-то не думал, пользуя от лени класс от tobject. Но если мне, допустим, нужно соорудить габаритный контейнер для листа карты, то удобнее делать класс (я так думаю):
                    ExpandedWrap disabled
                      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;
                      Цитата mnj @
                      Но если мне, допустим, нужно соорудить габаритный контейнер для листа карты, то удобнее делать класс (я так думаю):

                      А я так не думаю, поскольку TBorders - это фиксированная структура из 4-х точек, которые не имеет смысла выделять динамически по отдельности. Лучше заменить PMyPoint на TMyPoint, тогда и необходимость в деструкторе отпадет (и соотв-но в использовании класса). По моему для габаритной рамки удобнее использовать вариантную запись типа:
                      ExpandedWrap disabled
                        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).
                      Сообщение отредактировано: leo -
                        Цитата mnj @
                        Points, Triangles: TList;

                        В данном случае использовать стандартный TList неудобно, т.к. приходится писать кучу лишнего кода для удаления\освобождения его элементов. Лучше его переопределить, чтобы память под удаляемые элементы освобождалась автоматически. Для этого достаточно переопределить метод TList.Notify
                        ExpandedWrap disabled
                          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;
                        Такой список можно использовать для любых простых записей, не требующих финализации (т.е. не содержащих строк и т.п.), когда для освобождения элемента можно использовать FreeMem вместо Dispose (=Finalize+FreeMem)
                          Вообще, получается, что в моем случае классы, как строительный материал, не нужны, все можно сконструировать записями, тот же объект на карте:
                          ExpandedWrap disabled
                                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?
                            Цитата mnj @
                            Вообще, получается, что в моем случае классы, как строительный материал, не нужны, все можно сконструировать записями, тот же объект на карте:

                            Не нужно впадать в крайности. Не имеет смысла создавать классы для простых\примитивных структур типа точек, треугольников, габаритов и т.п. А TMapObject - довольно навороченная структура, которая к тому же может иметь (а может и не иметь) подобъекты SubObj аналогичной структуры, которые имеет смысл создавать динамически. Поэтому, на мой взгляд, лучше объявить TMapObject как класс, заменить тип SubObj на TObjectList и удалять его в деструкторе.

                            Цитата mnj @
                            А как очистить TList, заполненный такими TMapObject?

                            Если записи не простые (содержат строки, динамические массивы и т.п.), то удалять их нужно через Dispose(Тип_указателя_записи(Ptr));
                              Цитата leo @
                              Не нужно впадать в крайности. Не имеет смысла создавать классы для простых\примитивных структур типа точек, треугольников, габаритов и т.п.

                              Стараюсь :) . Но в тех же габаритах есть необходимость учитывать формат координат (градусы или метры) и производить манипуляции в зависимости от текущего формата. Получается, что нужен все-таки класс, чтобы воспользоваться полиморфизмом и определить метод TBorders.DoSomething для градусной нотации и для метрической.
                                Цитата mnj @
                                Но в тех же габаритах есть необходимость учитывать формат координат (градусы или метры) и производить манипуляции в зависимости от текущего формата. Получается, что нужен все-таки класс...

                                Не знаю. Всё таки TBorders - это не самостоятельная сущность, а просто свойство\характеристика объекта карты или карты в целом. Поэтому учет "DoSomething для градусной нотации и для метрической" может делаться в этих объектах, а не в самом TBorders. Всё зависит от конкретики (в частности от того, что действительно скрывается за DoSomething), иначе можно опять "впасть в крайность" и прийти к выводу, что точка и треугольник тоже должны быть классами, т.к. они также должны учитывать "градусную и метрическую нотацию".
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:
                                Страницы: (3) 1 [2] 3  все


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0835 ]   [ 19 queries used ]   [ Generated: 19.04.24, 17:45 GMT ]