Правильный free

Проектирование и разработка идеального средства программирования.

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

Правильный free

Сообщение DedFrend » 15.12.2018 01:10:38

Давняя и застарелая проблема.
Если на один объект есть несколько ссылок, то освобождение его с помощью одной ссылки ничего не делает с другими.
В смысле по другим ссылкам к нему по прежнему можно обращаться и во многих случаях результат будет, как будто никто его и не освобождал.
Специально написал простенький тест
pas
Код: Выделить всё
unit UfmTestFreeError;

{$MODE Delphi}
{$OBJECTCHECKS+}

interface

uses
  LCLIntf, LCLType, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, MaskEdit;

type
  TSimpleInt = class
  public
    Value: integer;
    constructor Init(V: integer);
    destructor Destroy; override;
  end;

  { TfmTestFreeError }

  TfmTestFreeError = class(TForm)
    bnCreate: TBitBtn;
    edResult: TEdit;
    bnRead: TBitBtn;
    Label1: TLabel;
    Label2: TLabel;
    medSource: TMaskEdit;
    bnFree: TBitBtn;
    procedure bnCreateClick(Sender: TObject);
    procedure bnReadClick(Sender: TObject);
    procedure bnFreeClick(Sender: TObject);
  private
    { Private declarations }
    SimpleInt: TSimpleInt;
    SimpleRef: TSimpleInt;
  public
    { Public declarations }
  end;

var
  fmTestFreeError: TfmTestFreeError;

implementation

{$R *.lfm}

{ TSimpleInt }

constructor TSimpleInt.Init(V: integer);
begin
  inherited Create;
  Value := V;
end;

destructor TSimpleInt.Destroy;
begin
  inherited;
  //Self := nil;
end;

procedure TfmTestFreeError.bnReadClick(Sender: TObject);
begin
  edResult.Text := IntToStr(SimpleRef.Value);
end;

procedure TfmTestFreeError.bnCreateClick(Sender: TObject);
begin
  SimpleInt := TSimpleInt.Init(StrToInt(medSource.Text));
  SimpleRef := SimpleInt;
end;

procedure TfmTestFreeError.bnFreeClick(Sender: TObject);
begin
  FreeAndNil(SimpleInt);
end;

end.


lfm

Код: Выделить всё
object fmTestFreeError: TfmTestFreeError
  Left = 436
  Height = 147
  Top = 200
  Width = 528
  Caption = 'Test Free Error'
  ClientHeight = 147
  ClientWidth = 528
  Color = clBtnFace
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  LCLVersion = '1.6.2.0'
  object edResult: TEdit
    Left = 64
    Height = 21
    Top = 48
    Width = 121
    TabOrder = 0
  end
  object bnRead: TBitBtn
    Left = 312
    Height = 25
    Top = 56
    Width = 121
    Caption = 'Read'
    OnClick = bnReadClick
    TabOrder = 1
  end
  object medSource: TMaskEdit
    Left = 64
    Height = 21
    Top = 16
    Width = 120
    CharCase = ecNormal
    MaxLength = 5
    TabOrder = 2
    EditMask = '!99999;1;_'
    Text = '     '
    SpaceChar = '_'
  end
  object bnFree: TBitBtn
    Left = 312
    Height = 25
    Top = 96
    Width = 121
    Caption = 'Free'
    OnClick = bnFreeClick
    TabOrder = 3
  end
  object Label1: TLabel
    Left = 8
    Height = 13
    Top = 20
    Width = 32
    Caption = 'Число'
    ParentColor = False
  end
  object Label2: TLabel
    Left = 8
    Height = 13
    Top = 56
    Width = 52
    Caption = 'Результат'
    ParentColor = False
  end
  object bnCreate: TBitBtn
    Left = 312
    Height = 25
    Top = 16
    Width = 121
    Caption = 'Create'
    OnClick = bnCreateClick
    TabOrder = 4
  end
end


Кнопкой Create создается объект и на него делается вторая ссылка.
Кнопкой Free первая ссылка освобождается
Кнопкой Read выводится содержимое объекта по второй ссылке. Легко убедится, что Read прекрасно работает после Free.
На разных платформах и в среде Delphi.

Меня удивляет не столько наличие проблемы, сколько отсутствие даже ее обсуждения.
Со своей стороны хочу предложить, чтобы при освобождении объекта , обнулялся его указатель self. Я попробовал это сделать самостоятельно, но при этой операции программа иногда падает, а иногда все равно все работает (правда, содержимое портится, но это несущественно).
Ну и понятно, что это должно делаться на системном уровне.
DedFrend
постоялец
 
Сообщения: 156
Зарегистрирован: 25.11.2018 12:21:50

Re: Правильный free

Сообщение MysticCoder » 15.12.2018 01:39:00

специально же повторяют, чтоб следили за своими переменными)
после освобождения класса память остается в распоряжении процесса, диспетчера памяти. отсюда и отсутствие AV. что в ней будет через секунду и будет ли она еще доступна - точно сказать нельзя.
Self - не существует сам по себе, он есть только в методах класса и существует только на время выполнения метода, поэтому смысла от его обнуления нет. Например, при вызове TSomeClass(Pointer(1234)).SomeMethod в метод передатся Self с адресом 1234.
нормальных решений этой ситуации кроме как следить за переменными так понимаю нет. Если забивать нулями память инстанса, то через некоторое время в этой памяти может быть что угодно. Если использовать интерфейсы - придется все ссылки освобождать. Можно вести глобальный список указателей на инстансы которые были отдестроены и перед вызовом каждого метода сравнивать Self со списком - муторно и ненадежно. Если принудительно освобождать память экземпляра класса из процесса - этому помещает гранулярность страниц памяти, т.е. за раз придется по 4Кб или может 64Кб освобождать, в зависимости от ОС, а в той же странице памяти могут быть и другие важные данные и инстансы классов.
MysticCoder
постоялец
 
Сообщения: 154
Зарегистрирован: 14.09.2013 00:20:28

Re: Правильный free

Сообщение Снег Север » 15.12.2018 07:59:19

Нет проблемы, поэтому нет и обсуждения. Необходимость программисту самому отслеживать ссылки - особенность паскаля.
Если нужна автоматизация - используют интерфейсы. Для проверки существования объекта - функция Assigned.
Аватара пользователя
Снег Север
долгожитель
 
Сообщения: 2993
Зарегистрирован: 27.11.2007 16:14:47

Re: Правильный free

Сообщение DedFrend » 15.12.2018 10:46:26

MysticCoder
Ну да, конечно, спасение утопающих - дело рук самих утопающих!
А вот TSomeClass передает указатель не на self , а на объект, т.е. на экземпляр класса. И self расположен где-то там, в этом объекте, и я не вижу причин, почему его нельзя обнулять.
Да, полное спокойствие дает только страховой полис, и в процессе работы там опять может оказаться какая-то хрень, но так как делается сейчас - там эта хрень гарантирована и, что еще хуже, неотличима от объекта до его разрушения.

Снег Север
А Assigned тут вообще не при делах. В том-то и печаль - в моем примере я как раз делаю на первой ссылке FreeAndNil, но вторая продолжает работать.
DedFrend
постоялец
 
Сообщения: 156
Зарегистрирован: 25.11.2018 12:21:50

Re: Правильный free

Сообщение olegy123 » 15.12.2018 11:07:25

DedFrend писал(а): В том-то и печаль - в моем примере я как раз делаю на первой ссылке FreeAndNil, но вторая продолжает работать.
расскажите нам о типе "сылка", как понимаешь - а мы посмеемся.

Добавлено спустя 1 минуту 50 секунд:
Снег Север писал(а):Необходимость программисту самому отслеживать ссылки - особенность паскаля.

в си ровно такая же история.
olegy123
долгожитель
 
Сообщения: 1643
Зарегистрирован: 25.02.2016 12:10:20

Re: Правильный free

Сообщение Ichthyander » 15.12.2018 11:43:51

Как правильно сказал есть интерфейс с подсчетом ссылок. Pascal боолее низкоуровневый язык, чем скриптовые типа Ява скрипт и т.д., в которых для каждого объекта интерпретатор сам заботится об освобождении объектов. Но никто не сказал, что подсчет ссылок реализовать нельзя. Просто нет смысла это делать априори для всех объектов. Эта забота ложится на программиста
Аватара пользователя
Ichthyander
энтузиаст
 
Сообщения: 668
Зарегистрирован: 04.04.2007 08:32:43
Откуда: Астрахань

Re: Правильный free

Сообщение Vadim » 15.12.2018 12:45:27

olegy123 писал(а):расскажите нам о типе "сылка", как понимаешь - а мы посмеемся.

А действительно, не потрудится ли топикстартер рассказать, что он имеет в виду... ;-)
DedFrend писал(а):В смысле по другим ссылкам к нему по прежнему можно обращаться и во многих случаях результат будет, как будто никто его и не освобождал.
Специально написал простенький тест

Обращаться то можно, вот только результат... ;-)
Я вот тоже написал простенький пример:
Код: Выделить всё
program p1;
Uses SysUtils, Classes;

type
  TSimpleInt = class
  public
    Value: integer;
    constructor Init(V: integer);
    destructor Destroy; override;
  end;

    constructor TSimpleInt.Init(V: integer);
    begin
      inherited Create;
      Value := V;
    end;

    destructor TSimpleInt.Destroy;
    begin
      inherited;
      //Self := nil;
    end;

var
  SimpleRef, SimpleInt: TSimpleInt;
 
 
Begin

  SimpleInt := TSimpleInt.Init(22);
  SimpleRef := SimpleInt;
  WriteLn('SimpleInt.Value = ', SimpleInt.Value);
  WriteLn('SimpleRef.Value = ', SimpleRef.Value);
 
  FreeAndNil(SimpleInt);

  WriteLn('Убили SimpleInt... ;-) ');
  WriteLn('SimpleRef.Value = ', SimpleRef.Value);
end.

И что при этом получил...
Вложения
07.png
07.png (8.04 КБ) Просмотров: 27241
Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Re: Правильный free

Сообщение serbod » 15.12.2018 17:02:04

Тема много раз обсуждалась и решение давно существует

viewtopic.php?f=1&t=25592&p=127579&hilit=WeakRef#p127579
Аватара пользователя
serbod
постоялец
 
Сообщения: 449
Зарегистрирован: 16.09.2016 11:03:02
Откуда: Минск

Re: Правильный free

Сообщение DedFrend » 16.12.2018 22:30:34

Извиняюсь за задержку. Был оффлайн.

Vadim » 15.12.2018 11:45:27

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

serbod » 15.12.2018 16:02:04

Вы это всерьез написали? То что предлагается в ссылке - это решение для фриков. Честно говоря даже если бы работало что-то вроде
Код: Выделить всё
if Assigned(Object) then Object.CallMethod;

меня бы это не устроило.
Кстати, подсчет ссылок - вообще хорошая вещь, но только пока у вас нет циклических ссылок.

А теперь для всех

Обращаю внимание, что тема открыта не в рубрике "потрепаться".
Хотелось бы серьезного обсуждения.
Уточняю постановку вопроса: Кто-нибудь понимает, почему НЕЛЬЗЯ ОБНУЛИТЬ SELF ПРИ ВЫЗОВЕ ДЕСТРУКТОРА?
И я имею в виду не программиста, а разработчиков.
Еще раз. Я не претендую на всеобъемлющее решение проблем работы с указателями. Что делать с указателями на массивы и записи я понятия не имею.
Но все классы являются потомками TObject и я не понимаю почему нельзя разрушить (хотя бы self обнулить) сам объект, а не только ссылку на него. Причем сделать это абсолютно прозрачно, не требуя от программиста дополнительной работы.

Глупо спорить с тем, что программист должен следить за созданием и освобождением. Но человеку свойственно ошибаться, так почему же не помочь ему обнаружить ошибку?
DedFrend
постоялец
 
Сообщения: 156
Зарегистрирован: 25.11.2018 12:21:50

Re: Правильный free

Сообщение Дож » 16.12.2018 22:52:20

Уточняю постановку вопроса: Кто-нибудь понимает, почему НЕЛЬЗЯ ОБНУЛИТЬ SELF ПРИ ВЫЗОВЕ ДЕСТРУКТОРА?

Потому что для этого потребовалось бы поддерживать список всех ссылок на уничтожаемый объект, это очень долго.
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: Правильный free

Сообщение DedFrend » 16.12.2018 23:39:18

Дож » 16.12.2018 21:52:20
Потому что для этого потребовалось бы поддерживать список всех ссылок на уничтожаемый объект, это очень долго.

А вот это - прямое заблуждение. Не надо путать указатели на объект (которых действительно м.б. много) с указателем self, который у объект один.
DedFrend
постоялец
 
Сообщения: 156
Зарегистрирован: 25.11.2018 12:21:50

Re: Правильный free

Сообщение zub » 17.12.2018 00:34:29

DedFrend
А теперь для вас персонально:
>>А вот это - прямое заблуждение. Не надо путать указатели на объект (которых действительно м.б. много) с указателем self, который у объект один.
Ознакомтесь как оно работает и не порите чушь
zub
долгожитель
 
Сообщения: 2884
Зарегистрирован: 14.11.2005 23:51:26

Re: Правильный free

Сообщение Дож » 17.12.2018 01:42:59

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

Конкретно Self деструктором не обнуляется, потому что он может не быть областью памяти, а быть вычислен ("rvalue") в конструкциях вида ObjA.GetObjB.Free, он может быть с модификатором const и т.д. Решается использованием FreeAndNil.

Мой предыдущий ответ был про то, почему другие ссылки на объект не обнуляются и поддерживать такое в языке плохая идея.
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: Правильный free

Сообщение Vadim » 17.12.2018 03:58:39

DedFrend писал(а):Действительно, в консоли значение разрушается, но для консоли и вопрос не актуален. Для винды консоль - что-то вроде динозавра.

А при чём тут консоль? Ведь речь шла про объект.
Должен ли я Вас понимать так, что в консоли значение разрушается, а в GUI не разрушается?
Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Re: Правильный free

Сообщение DedFrend » 17.12.2018 08:22:15

Vadim » 17.12.2018 02:58:39
а в GUI не разрушается?

Ну а зачем же я пример программы приводил? В GUI после Free тоже самое значение остается.
DedFrend
постоялец
 
Сообщения: 156
Зарегистрирован: 25.11.2018 12:21:50

След.

Вернуться в Компилятор / язык программирования

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 2

Рейтинг@Mail.ru