Как убедиться, что объект не уничтожен

Общие вопросы программирования, алгоритмы и т.п.

Модератор: Модераторы

Ответить
Аватара пользователя
Brainenjii
энтузиаст
Сообщения: 1351
Зарегистрирован: 10.05.2007 00:04:46

Как убедиться, что объект не уничтожен

Сообщение Brainenjii »

Сабж ^_^ Assigned и проверка на nil не помогают.

Код: Выделить всё

Var
  i: Integer;
  aList: TList;
  aObject1: TObject;
Begin
  aList := TList.Create;
  aObject1 := TObject.Create;
  aList.Add(aObject1);
  FreeAndNil(aObject1);
  For i := 0 To aList.Count - 1 Do
    Try
      If Assigned(aList[i]) And Not(aList[i] = nil) Then // <- Не поможет
        Write(TObject(aList[i]).ToString);
    Except On E: EAccessViolation Do
      WriteLn('Не помогло :-(');
    End;
  aList.Free;
End.
MageSlayer
постоялец
Сообщения: 216
Зарегистрирован: 07.09.2006 12:30:44

Сообщение MageSlayer »

Brainenjii писал(а):Сабж ^_^ Assigned и проверка на nil не помогают.

Разумеется не помогают. И не помогут. Не делайте этого.

Если вам в нужно что-то такое в паскале, то это значит одно из следующего:
-вы не понимаете, что вы делаете и рано или поздно ваша "архитектура" развалится;
-вместо интерфейсов вы неправильно выбрали объекты;
-вы неверно выбрали язык.
Odyssey
энтузиаст
Сообщения: 580
Зарегистрирован: 29.11.2007 16:32:24

Сообщение Odyssey »

MageSlayer прав.

Здесь важно помнить, что aObject1 и aList[0] -- это два разных указателя на один и тот же объект в куче. FreeAndNil освобождает объект (т.е. кусок памяти в куче) и обнуляет один из указателей, но не второй. Второй после уничтожения объекта продолжает указывать в область памяти, которая уже не принадлежит объекту (и возможно, принадлежит кому-то другому).

Если нужно работать с несколькими указателями на один и тот же объект, я обычно делаю так:
- мысленно выделяю из всех указателей один "основной" (условно считаю, что это и есть сам объект). Он создаётся через конструктор и освобождается через FreeAndNil. Просто присвоить ему nil (или любое другое значение) нельзя, это приведёт к утечке памяти.
- все остальные указатели на этот объект считаются ссылками. Им можно присваивать только указатели на уже существующие объекты, либо nil. Выделение/освобождение памяти через конструктор/деструктор для ссылок запрещено, это приведёт к утечкам памяти/EAccessViolation.
- выделяю экземпляр класса (например, TObjectList), ответственный за операции с "самими" объектами. Удалить существующий объект из памяти можно только через запрос к этому классу (напр. TObjectList.Delete). Перед удалением самого объекта необходимо удалить, либо обнулить ( := nil) все ссылки на этот объект.
- обозначаю переменную-"сам объект" и переменные-ссылки на уровне текста программы: комментариями или именованием (напр. MyObject, MyObjectRef).
Аватара пользователя
debi12345
долгожитель
Сообщения: 5761
Зарегистрирован: 10.05.2006 23:41:15
Откуда: Ташкент (Узбекистан)

Сообщение debi12345 »

Разумеется не помогают. И не помогут. Не делайте этого.

Частный случай, ИМХО.
Почему бы самому не писать custom- классы - деструкторы которых (на FreeAndNil) освобождают все внутренние динамические объекты ?
Maxizar
постоялец
Сообщения: 385
Зарегистрирован: 20.03.2010 18:48:14

Сообщение Maxizar »

Может я не понял вопроса, но почему все говояр мол так нельзя??? Непонятно, вообще-то льзя, но ошибка из-за вот этого:

Код: Выделить всё

  FreeAndNil(aObject1);

После этого в листе у нас указатель есть (ведь его никто не дулалил нетак ли) и он указывал в свое время до ФрииАндНил на наш класс (объект) а после? правильно он указывает тудаже, но там уже тю тю...

Или в чем был вопрос то?
Просто если вы ждали, что при удалении объекта обнулится поинтер в листе?
Просто судя по этому:

Код: Выделить всё

Assigned(aList[i]) And Not(aList[i] = nil)

Вы именно этого ждали?
Если да, то одисей уже написал, про указатель ссылок и все такое, если просто хотите юзать списки (TList) То я делаю как то так:

Код: Выделить всё

unit Unit1; 

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  Form1: TForm1;
  List:TList;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.Button2Click(Sender: TObject);  //Удаления последнего (хвоста)
begin
 if (List<>Nil) and (List.Count>0) then
  begin
    TForm(List[List.Count-1]).Free;
    List.Delete(List.Count-1);              //Обязательно иначе полная чушь
  end;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
 if (List<>Nil) and (List.Count>0) then
   TForm(List[List.Count-1]).Show;               //Показываем хвостик
end;

procedure TForm1.Button1Click(Sender: TObject);  //Создание в данном случае 10 окон
var I:Integer;
    NF:TForm;
begin

  if List = nil then
    List:=TList.Create;

  For I:=1 to 10 do
   begin
     NF:=TForm.Create(Nil);
     NF.Caption:=IntToStr(I);
     List.Add(NF);
   end;
end;

end.


Для Объектов типа Object

Код: Выделить всё

unit Unit1; 

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;


  { TComplexObject }
  PMyObject=^TMyObject;
  TMyObject = Object
    I:Integer;
    S:String;
    constructor Create(vI:Integer; vS:String);
  end;

var
  Form1: TForm1;
  List:TList;

implementation

{ TComplexObject }

constructor TMyObject.Create(vI:Integer; vS:String);
begin
   I:=vI;
   S:=vS;
end;

{$R *.lfm}

{ TForm1 }

procedure TForm1.Button2Click(Sender: TObject);  //Удаления последнего (хвоста)
begin
 if (List<>Nil) and (List.Count>0) then
  begin
    Dispose(PMyObject(List[List.Count-1]));
    List.Delete(List.Count-1);              //Обязательно иначе полная чушь
  end;
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
 if (List<>Nil) and (List.Count>0) then
   Caption:= PMyObject(List[List.Count-1])^.S;               //Показываем хвостик
 //Либо так Caption:= TMyObject(List[List.Count-1]^).S;
end;

procedure TForm1.Button1Click(Sender: TObject);  //Создание в данном случае 10 окон
var I:Integer;
    NO:PMyObject;
begin

  if List = nil then
    List:=TList.Create;

  For I:=1 to 10 do
   begin
     NO:=New(PMyObject);
     NO^.Create(I, 'Строка№ '+IntToStr(I));
     List.Add(NO);
   end;
end;

end.


PS. Что-то я перегрелся, даже вопросы недопонимаю (:...
Аватара пользователя
stikriz
энтузиаст
Сообщения: 612
Зарегистрирован: 15.03.2006 08:37:47

Сообщение stikriz »

Хороший препод на третьем курсе должен подойти к человеку, который не понимает указателей и сказать, что надо переходить на поток менеджеров. В программерах делать нечего.
По существу. Только интерфейсы помогут, внимание, НЕ УДАЛИТЬ объект, если он присвоен действительной используемой переменной.
Понять, что адрес в указателе указывает на уничтоженный или не уничтоженный объект невозможно.
У меня два указателя, и я FreeAndNil сделал с одним из них... И что во втором? Некий недествительный адрес...

Добавлено спустя 2 минуты 42 секунды:
Кстати, можно в менеджере памяти сделать проверку указателя на дейстывительность. Но, это, по моему, крайне неэффкутивно. Видимо, нужно еще и список какой создать на каждый указатель, а при FreeMem искать этот указатель и удалять из списка... Хрень. Лучше осторожно пользоваться указателями.
Аватара пользователя
hinst
энтузиаст
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Сообщение hinst »

Собственно, а TObjectList на щито? он разве не занимается тем, что настраивает там какие-то указатели на события, и каким-то образом когда объект Free, он его удаляет из списка? По-моему, если использовать TObjectList в примере автора, всё сразу станет хорошо. А если не станет, то TComponentList уж точно должен помочь (помню, был такой)
Аватара пользователя
Brainenjii
энтузиаст
Сообщения: 1351
Зарегистрирован: 10.05.2007 00:04:46

Сообщение Brainenjii »

Ну, мало ли, вдруг как-то решена проблема, а я и не знал йцу Вообще, что мешает один бит адреса использовать под флаг "актуальности" объекта. 32 битные ОСи (в большинстве своем) все-равно разрешают только 2 Гб использовать, а между 2^63 и 2^64 на практике и разницы нет ^_^ Или там забивать при разрушении область объекта тем же deadbeef'ом, и при обращении проверять...
Ну, нет так нет, печально ^_^
P.S. проблема, которая побудила к старту топика уже решена некоторым подобием сборщика мусора - вместо уничтожения ставил флаг "удалить", и в определённый момент пробегался по всем нужным спискам и удалял из них объекты и только после этого освобождал... Но все-равно, принципиальная невозможность проверки корректности ссылки на объект огорчает
Аватара пользователя
stikriz
энтузиаст
Сообщения: 612
Зарегистрирован: 15.03.2006 08:37:47

Сообщение stikriz »

Есть такой паттерн, что есть кто-то, кто создал объект. У него может быть список объектов, которые он создал. Когда объект уничтожается, то он лезет в этот список и удаляет самого себя из него. И все - мы всегда знаем, кого уж нет...
Аватара пользователя
Brainenjii
энтузиаст
Сообщения: 1351
Зарегистрирован: 10.05.2007 00:04:46

Сообщение Brainenjii »

У меня почти так и есть (объект сам себя, правда добавляет в этот общий список).
Odyssey
энтузиаст
Сообщения: 580
Зарегистрирован: 29.11.2007 16:32:24

Сообщение Odyssey »

hinst писал(а):Собственно, а TObjectList на щито? он разве не занимается тем, что настраивает там какие-то указатели на события, и каким-то образом когда объект Free, он его удаляет из списка?

TObjectList - наоборот. Когда объект удаляют из списка (TObjectList), то этот список освобождает объект, если у списка OwnsObjects = True. А если мы сами освободим объект, позже получим Access Violation.

TComponentList - да, ведёт себя именно так (судя по документации). Вот он как раз и реализует
stikriz писал(а):паттерн, что есть кто-то, кто создал объект. У него может быть список объектов, которые он создал. Когда объект уничтожается, то он лезет в этот список и удаляет самого себя из него.

Но чтобы этим воспользоваться, все свои классы, которые складываются в список, придётся наследовать от TComponent.
wavebvg
постоялец
Сообщения: 355
Зарегистрирован: 28.02.2008 03:57:35

Сообщение wavebvg »

Можно добавить указатель на "уникальный" хеш в базовый объект (деструктор объекта хеш "портит"), тогда, проверив этот хеш можно узнать, существует ли объект или нету (Правда будет существовать определенная вероятность, что хеш совпадет случайно).
Вот только данная необходимость при правильном проектировании возникнуть не должна...
Mr.Smart
долгожитель
Сообщения: 1796
Зарегистрирован: 29.03.2008 00:01:11
Откуда: из леса!

Сообщение Mr.Smart »

wavebvg угу и словить эксепшен доступа к памяти.
Аватара пользователя
hinst
энтузиаст
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Сообщение hinst »

Говорю вам, TComponentList лучше всех. Всегда его заюзываю!
wavebvg
постоялец
Сообщения: 355
Зарегистрирован: 28.02.2008 03:57:35

Сообщение wavebvg »

Mr.Smart писал(а):wavebvg угу и словить эксепшен доступа к памяти.

Ну это надо ещё постараться, поскольку память выделяется не побитно
Ответить