Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2007.12.02;
Скачать: [xml.tar.bz2];

Вниз

Проблема с TFileStream   Найти похожие ветки 

 
ProgRAMmer Dimonych ©   (2007-11-05 22:45) [0]

Сталкивался много раз, сейчас решил разобраться :)

Дело в том, что TFileStream как-то странно себя ведёт. Даже, я бы сказал, непредсказуемо, т.е. не так, как должен был бы.

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

Tmp:TFileStream;

Забавно то, что в начале процедуры этот самый Tmp показывается (в Delphi"овом Hint"е) со свойством (Handle:12) и таким же остаётся после вызова его метода Free. То же самое с FileStream"ом, который блокирует файл до тех пор, пока он нужен моему объекту, и который объявлен как поле объекта.

Самое печальное, что, похоже, именно из-за такого поведения возникают AV и External Errors. Посмотрел исходник TFileStream"а и справку по Дельфям: в деструкторе TFileStream"а вызывается функция FileClose, которая, если верить справке, просто принимает Handle файла, что, на мой взгляд, имеет печальные последствия:

destructor TFileStream.Destroy;
begin
 if FHandle >= 0 then FileClose(FHandle);
 inherited Destroy;
end;


Т.е. FHandle передаётся не как var-параметр, а inherited Destroy тянется аж от TObject, где он virtual.

Попробовал копнуть в сети, но русскоязычного ничего не нашёл. Насколько мне позволяет судить моё знание английского, что-то по этой проблеме есть здесь: http://www.delphipraxis.net/topic77280.html. Если я правильно понимаю немецкий, то вместо метода Free следует юзать процедуру FreeAndNil().

Это так?


 
vpbar ©   (2007-11-05 22:58) [1]


> Забавно то, что в начале процедуры этот самый Tmp показывается
> (в Delphi"овом Hint"е) со свойством (Handle:12) и таким
> же остаётся после вызова его метода Free.

Ну в чем проблема? То что отладчик делфи показывает поля объекта после его освобождения? Так ведь память после освобождения объекта не сразу то затирается.


 
homm ©   (2007-11-05 23:02) [2]

> [0] ProgRAMmer Dimonych ©   (05.11.07 22:45)
> Если я правильно понимаю немецкий, то вместо метода Free
> следует юзать процедуру FreeAndNil().

А при чем здесь TFileStream?


 
ProgRAMmer Dimonych ©   (2007-11-05 23:12) [3]

> vpbar ©   (05.11.07 22:58) [1]
> Ну в чем проблема? То что отладчик делфи показывает поля
> объекта после его освобождения? Так ведь память после освобождения
> объекта не сразу то затирается.

Да вот ведь вещь: не всегда так, иногда nil. Но это, наверное, не основное. Хотя какое не сразу? Если объект удалён, то вместо Объект=(Свойство:Значение; Свойство:Значение) там должно по идее отображаться Объект=nil.

> homm ©   (05.11.07 23:02) [2]
> > [0] ProgRAMmer Dimonych ©   (05.11.07 22:45)
> А при чем здесь TFileStream?

По приведённой мной ссылке - ветка форума, где, насколько я могу судить, пытаясь вникнуть в суть написанного на незнакомом мне языке, обсуждается проблема использования функции Assigned() по отношению к экземпляру TFileStream. Вроде как вечно True возвращает, а это уже грозит AV"ами и прочими радостями. Если сформулировать мой вопрос "в лоб", то: как правильно удалять TFileStream?


 
Anatoly Podgoretsky ©   (2007-11-05 23:16) [4]


> Если объект удалён, то вместо Объект=(Свойство:Значение;
>  Свойство:Значение) там должно по идее отображаться Объект=nil.
>

А все ветки об этом говорят, что это ересь и блажь.


 
Юрий Зотов ©   (2007-11-05 23:17) [5]

> что, на мой взгляд, имеет печальные последствия

Файл закрывается и освобождается на уровне системы. В какой-то там программе остается какая-то там переменная, значение которой чему-то там равно.

Ну и что? Какие же тут печальные последствия?


 
Юрий Зотов ©   (2007-11-05 23:24) [6]

> ProgRAMmer Dimonych ©   (05.11.07 23:12) [3]

> как правильно удалять TFileStream?

Точно так же, как и ЛЮБОЙ другой объект - вызовом Free.

Если при удалении объекта нужно еще и обнулить какую-то переменную, то можно вызвать FreeAndNil. Она выполнит 2 операции - и объект уничтожит (тем же вызовом Free), и переменную обнулит. Причем это ДВЕ совершенно РАЗНЫЕ операции. Их можно выполнить и самому, ручками.


 
ProgRAMmer Dimonych ©   (2007-11-05 23:27) [7]

ОК, даю кусок кода...

function TMyObject.SaveToFile(FileName:ANSIString):Boolean;
var
Tmp:TFileStream;
FN:ANSIString;
i:Integer;
N:Word;
begin
Result:=False;
FN:=TempDir+ExtractFileName(FileName)+"~";
DeleteFile(PANSIChar(FN));
try Tmp:=TFileStream.Create(FN,fmCreate); except Exit; end;
{Пишем в файл}
if Assigned(Tmp) then Tmp.Free;
===>>> if Assigned(FFile) then FFile.Free; {Здесь вызывается Free}
if FFileName=FileName then DeleteFile(PANSIChar(FFileName));
RenameFile(FN,FileName);
Self.LoadFromFile(FileName);
Result:=True;
end;

function TMyObject.LoadFromFile(FileName:ANSIString):Boolean;
var
N:Word;
i:Integer;
begin
Result:=False;
===>>> if Assigned(FFile) then FFile.Free; {А здесь у нас AV}
{Чтение файла, которого нет из-за AV}
end;


Что скажете?


 
vpbar ©   (2007-11-05 23:28) [8]

жуть


 
ProgRAMmer Dimonych ©   (2007-11-05 23:29) [9]

> Юрий Зотов ©   (05.11.07 23:24) [6]

Вы меня опередили. Но по этому куску кода без FreeAndNil - никуда. Так ведь? Ну, если не считать саморучно написанной замены.


 
ProgRAMmer Dimonych ©   (2007-11-05 23:30) [10]

P.S. К фрагменту кода: FFile объявлена в секции private TMyObject и предназначена для того, чтобы удерживать используемый файл.


 
Anatoly Podgoretsky ©   (2007-11-05 23:30) [11]


> Что скажете?

Это действительно Кусок.


 
ProgRAMmer Dimonych ©   (2007-11-05 23:33) [12]

> Anatoly Podgoretsky ©   (05.11.07 23:30) [11]
> > Что скажете?
> Это действительно Кусок.

Сокращаю, оставляю только работу с TFileStream"ом.

function TMyObject.SaveToFile(FileName:ANSIString):Boolean;
begin
Result:=False;
===>>> if Assigned(FFile) then FFile.Free; {Здесь вызывается Free}
Self.LoadFromFile(FileName);
Result:=True;
end;

function TMyObject.LoadFromFile(FileName:ANSIString):Boolean;
begin
Result:=False;
===>>> if Assigned(FFile) then FFile.Free; {А здесь у нас AV}
{Чтение файла, которого нет из-за AV}
end;


 
Anatoly Podgoretsky ©   (2007-11-05 23:34) [13]

> ProgRAMmer Dimonych  (05.11.2007 23:33:12)  [12]

===>>> if Assigned(FFile) then FFile.Free; {Здесь вызывается Free}

Это глупый код, правильный просто FFile.Free


 
Anatoly Podgoretsky ©   (2007-11-05 23:35) [14]

> ProgRAMmer Dimonych  (05.11.2007 23:33:12)  [12]

function TMyObject.LoadFromFile(FileName:ANSIString):Boolean;
begin
Result:=False;

А нафига здесь функция, которая всегда возвращает False


 
vpbar ©   (2007-11-05 23:37) [15]

Не в том смысле "Кусок", наверно Анатолий имел ввиду.
Просто непонятно кто с FFile еще работает. А вообще не обращайтесь к объектам после удаления. Для надежность обниляйте их.

{Чтение файла, которого нет из-за AV} - это как. еслибы не было ав вайл был бы?


 
Anatoly Podgoretsky ©   (2007-11-05 23:39) [16]


> Не в том смысле "Кусок", наверно Анатолий имел ввиду.

В смысле, что про него больше нечего сказать.


 
Юрий Зотов ©   (2007-11-05 23:42) [17]

> ProgRAMmer Dimonych ©   (05.11.07 23:27) [7]

В SaveToFile вызывается FFile.Free. При этом объект, на который указывала ссылка FFile, уничтожился, а значение самой переменной FFile не изменилось (потому что его никто не менял, а переменные сами по себе своих значений, к счастью, менять не умеют).

Долее вызывается LoadFromFile(FileName), в котором есть строчка:
if Assigned(FFile) then FFile.Free

Поскольку значение самой FFile не изменилось, Assigned дает true и делается попытка вызвать метод объекта Free через ссылку на уже уничтоженный экземпляр объекта. Здравствуй, Access Violation.

И никакие наличия-отсутствия файла здесь абсолютно ни при чем. Никакие файлы здесь вообще ни при чем.

> Что скажете?

1. Код отвратительный.
2. Непонимание указателей.
3. Непонимание работы объекта TFileStream с файловой системой.


 
ProgRAMmer Dimonych ©   (2007-11-05 23:44) [18]

> Anatoly Podgoretsky ©   (05.11.07 23:34) [13]
> Это глупый код, правильный просто FFile.Free

Согласен, автоматом написал, не вникнув.

> Anatoly Podgoretsky ©   (05.11.07 23:35) [14]
> А нафига здесь функция, которая всегда возвращает False

Это урезанный вариант функции, результат зависит от того, как пройдёт чтение из файла.

> vpbar ©   (05.11.07 23:37) [15]
> Просто непонятно кто с FFile еще работает.

FFile в начале функции проассоциирован с текущим файлом. Между двумя указанными мной строками возникает проблема. Все действия над FFile между этими строками я оставил, убрал только то, что не относится к нему. Объект не пользуется никаких там thread"ов и прочей радости.

> А вообще не обращайтесь к объектам после удаления. Для надежность
> обниляйте их.

FFile.Free не должно вызывать ошибку, т.к. Free - это, если верить справке, безопасная замена для Destroy. Но фишка именно в том, что Assigned(FFile) даёт True даже после вызова метода FFile.Free. Т.е., похоже, опять приходим к FreeAndNil. А вот как это отразится на использовании памяти, не будет ли утечек при таком подходе?

> {Чтение файла, которого нет из-за AV} - это как. еслибы
> не было ав вайл был бы?

Это значит, если бы не было AV, было бы чтение.


 
ProgRAMmer Dimonych ©   (2007-11-05 23:47) [19]

> Юрий Зотов ©   (05.11.07 23:42) [17]

Кажется, понял. Тогда получается, что, действительно, без присвоения указателю FFile:=nil будет глючить. Так вроде?


 
Юрий Зотов ©   (2007-11-05 23:47) [20]

> Но фишка именно в том, что Assigned(FFile) даёт True даже после вызова
> метода FFile.Free.

Димоныч, вот скажи мне простым человеческим языком - а почему после вызова FFile.Free вызов Assigned(FFile) вдруг должен дать False? Вот с какой такой радости?


 
korneley ©   (2007-11-05 23:48) [21]

За последние недели, раз в третий (или четвертый?) читаю: "я экземляр объекта освободил/открепил, а ссылка на него чего-то не нил..." А ты ея занилил? Ссылку-то? Хоть FreeAndNill, хоть := nill;... Просто не грабли, а ГРАБЛИ какие-то... Как у Хармса: "...а старушки всё падали..."


 
vpbar ©   (2007-11-05 23:51) [22]


> FFile.Free не должно вызывать ошибку, т.к. Free - это, если
> верить справке, безопасная замена для Destroy. Но фишка
> именно в том, что Assigned(FFile) даёт True даже после вызова
> метода FFile.Free. Т.е., похоже, опять приходим к FreeAndNil.
>  А вот как это отразится на использовании памяти, не будет
> ли утечек при таком подходе?

Вы не понимаете что происходит при вызове FFile.Free.
При этом вызывается деструктор освобождается память на которую указывет FFile. А вот само значение FFile не меняется. Т.е. оно указывает на уже освобожденную память. Assigned(FFile) - естественно ведь значение FFile не меняли и оно не равно nil. Приходим к FreeAndNil. Т.е. если возможно после освобождения обращение а этой переменной нужно обнилять ее. Что и делает FreeAndNil. И никаких утечек FreeAndNil не может внести


 
korneley ©   (2007-11-05 23:51) [23]

Упс... "нил" с одним английским "L" пишется... В отличие от "NULL" :)


 
ProgRAMmer Dimonych ©   (2007-11-05 23:51) [24]

> korneley ©   (05.11.07 23:48) [21]
> За последние недели, раз в третий (или четвертый?) читаю:
>  "я экземляр объекта освободил/открепил, а ссылка на него
> чего-то не нил..." А ты ея занилил? Ссылку-то? Хоть FreeAndNill,
>  хоть := nill;... Просто не грабли, а ГРАБЛИ какие-то...
>  Как у Хармса: "...а старушки всё падали..."

Ну, бывает, чего уж там. :)

P.S. Смайлик для виноватой улыбки вспомнить не получилось.


 
vpbar ©   (2007-11-05 23:52) [25]

>>korneley ©   (05.11.07 23:48) [21]
Нда. Намек разработчикая языка сделать так чтобы метод Free обнилял переменную. Ничего сложного для разработчиков.


 
korneley ©   (2007-11-05 23:58) [26]


> ... сделать так чтобы метод Free обнилял переменную...
А если ссылок много? (на то они и ссылки) Как прикажете их искать с целью дальнейшего за(об)ниления?


 
Anatoly Podgoretsky ©   (2007-11-06 00:02) [27]

> vpbar  (05.11.2007 23:52:25)  [25]

Да ну


 
Юрий Зотов ©   (2007-11-06 00:03) [28]

Димоныч, вот смотри.

Объект - это, грубо говоря, кусок динамической памяти. В твоей программе может быть хоть сто указателей на этот кусок. Хоть миллион. А может быть и один. Дело не в этом, а в том, что сам объект об этих указателях ничего не знает и знать не может.

ОК, вот теперь ты этот объект грохнул. Как он может при своем грохании обнулить все ссылки на себя, ежели он об этих ссылках ничего не знает?

Не может он их очистить. Он и не чистит. И ты сам - тоже не чистишь. Значит - что? значит, все эти указатели какими были, такими и остались, раз их никто не менял.

А куда теперь они стали указывать? А туда же, куда и раньше указывали - но теперь попытка обратиться по этому адрусу даст AV, потому что память-то по нему уже освобождена!!! Попытка обращения к нераспределенной области динамической памяти.

Теперь - что делают Free, FreeAndNil и Assigned?

Free проверяет, равен ли Self nil"у. Если нет - вызывает деструктор, если нет - не делает ничего. Это просто безопасный вызов деструктора.

FreeAndNil выполняет 2 независимые операции: вызывает Free и обнуляет переданный ей указатель на объект. То же самое ты можешь сделать и сам.

Assigned - просто проверяет, равна ли переданная ей ссылка nil"у. Больше ничего.

Теперь въехал?
:о)


 
vpbar ©   (2007-11-06 00:03) [29]


> korneley ©   (05.11.07 23:58) [26]

Ну тогда ссылки считать, и не освобождать пока все ссылки не обнулятся.

> А если ссылок много? (на то они и ссылки) Как прикажете
> их искать с целью дальнейшего за(об)ниления?
Хотя можно и искать. С объектом связть список. В который добавлять указатель на переменную при присваивании объекта. Тока тут тоже проблемы могут всякие.


 
Юрий Зотов ©   (2007-11-06 00:07) [30]

vpbar ©   (05.11.07 23:52) [25]

> Ничего сложного для разработчиков.

В самом деле? Тогда один простенький вопросик можно? А каким-таким волшебным образом в методе Free объекта можно узнать о том, что такая переменная вообще существует?

А ведь их может быть и не одна, запросто.


 
korneley ©   (2007-11-06 00:10) [31]


> С объектом связть список. В который добавлять указатель
> на переменную при присваивании объекта.
"Догнать Савранского - это утопия..." (с) :)


 
Германн ©   (2007-11-06 00:13) [32]


> Юрий Зотов ©   (06.11.07 00:03) [28]

А с телефонной книжкой разъяснение было интереснее :-)


 
vpbar ©   (2007-11-06 00:13) [33]


> Юрий Зотов ©   (06.11.07 00:07) [30]
> А каким-таким волшебным образом в методе Free объекта можно
> узнать о том, что такая переменная вообще существует?
Передавать в free указатель на переменную.

>>А ведь их может быть и не одна, запросто.
Вот это некоторая проблема.
Как вариант. Добавить в объект указатель на список ссылок на переменные (далее ССП).
var a,b,c:TObject;
....
a:=TObject.Create; // в том числе добавляем ссылку на a в ССП
b:=a; // добавляем ссылку на b в ССП
....
c:=b; // добавляем ссылку на c в ССП
b:=nil ; // удаляем ссылку на b из ССП
c.free; // освобождаем и обнуляем все ссылки из ССП
//
Про многопоточность думать еще надо. Хотя тоже решаемо.


 
ProgRAMmer Dimonych ©   (2007-11-06 00:14) [34]

> Юрий Зотов ©   (06.11.07 00:03) [28]

Не беспокойтесь Вы так, общее представление у меня есть. Скорее всего, с толку сбило то, что обычно не пользуюсь вот такими межпроцедурными вещами типа FFile в этом случае. Т.е. обычно Tmp:=TStringList.Create; в начале процедуры и Tmp.Free; в конце - и всё классно. А тут чего-то просто не подумал.

> vpbar ©   (06.11.07 00:03) [29]
> Ну тогда ссылки считать, и не освобождать пока все ссылки
> не обнулятся.

В COM, кажется, так делается?

===

А вообще - всем спасибо, по моему вопросу - всё.


 
vpbar ©   (2007-11-06 00:16) [35]

>>ProgRAMmer Dimonych ©   (06.11.07 00:14) [34]
>В COM, кажется, так делается?
Ага. В делфи, тоже, но только для интерфейсов.


 
Юрий Зотов ©   (2007-11-06 00:24) [36]

> vpbar ©   (06.11.07 00:13) [33]

a:=TObject.Create; // в том числе добавляем ссылку на a в ССП

КАК?

Каким образом объект в своем конструкторе может узнать о том, что где-то там вообще существует какая-то переменная a?

Тем более, узнать ее адрес.


 
Юрий Зотов ©   (2007-11-06 00:25) [37]

> > vpbar ©   (06.11.07 00:13) [33]
b:=a; // добавляем ссылку на b в ССП
А это и тем более фантастика.


 
ProgRAMmer Dimonych ©   (2007-11-06 00:27) [38]

> Юрий Зотов ©   (06.11.07 00:24) [36]
> Каким образом объект в своем конструкторе может узнать о
> том, что где-то там вообще существует какая-то переменная
> a?
>
> Тем более, узнать ее адрес.

> Юрий Зотов ©   (06.11.07 00:25) [37]
> > > vpbar ©   (06.11.07 00:13) [33]
> b:=a; // добавляем ссылку на b в ССП
> А это и тем более фантастика.

Компилятор нагрузить этим делом. Ну, в смысле этот, как его? Который про адреса переменных всё знает? Линковщик. :)


 
vpbar ©   (2007-11-06 00:28) [39]

>>Юрий Зотов ©   (06.11.07 00:24) [36]
Гм. А как объект знает какого он типа? И какую виртуальную функцию надо вызывать?
Компилятор добавит нужную информацию.


 
vpbar ©   (2007-11-06 00:31) [40]

Юрий, я уже начинаю сомневатся в своей правоте :) Хотя и не привык слепо верить авторитетам. Просто не пойму, что тут фантастичного.
ЗЫ. Видимо пора спать, чтобы червь сомнений не мутил мой светлый разум. :)


 
ProgRAMmer Dimonych ©   (2007-11-06 00:33) [41]

> vpbar ©   (06.11.07 00:31) [40]
> ЗЫ. Видимо пора спать, чтобы червь сомнений не мутил мой
> светлый разум. :)

Червь - не самое страшное. Вон троянцы тоже спали, а в это время троянский конь сработал. :)


 
korneley ©   (2007-11-06 00:37) [42]


> vpbar ©   (06.11.07 00:13) [33]

>  Добавить в объект указатель на список ссылок на переменные
Пусть даже имеется "внутренний", только для системы "ССП". Как отследить все перемещения/присвоения ссылок и отличить их, скажем, от арифметических операций? b := $01234567; это адрес или нет?

>А ведь их может быть и не одна, запросто
> Вот это некоторая проблема
:))) Некоторая... :))) И не единственная... :))) Кроме ошибки в коде... генетическом...


 
vpbar ©   (2007-11-06 00:39) [43]

>>korneley ©   (06.11.07 00:37) [42]
Ндя, если присваивать переменной типа TObject число - это точно ошибка в геноме.


 
korneley ©   (2007-11-06 00:49) [44]


> Ндя, если присваивать переменной типа TObject число - это
> точно ошибка в геноме.
А что мешает? В сысле не генома, а присвоения? Что хотим, то и храним в отведённых нам 32-х разрядах. Хоть четыре восьмибитных символа. Если правила позволяют - значит можно. Или ни разу не использовали TString.Objects[i] := TObject(<целое число>); ?


 
Германн ©   (2007-11-06 01:06) [45]


> korneley ©   (06.11.07 00:49) [44]
>
>
> > Ндя, если присваивать переменной типа TObject число -
> это
> > точно ошибка в геноме.
> А что мешает? В сысле не генома, а присвоения? Что хотим,
>  то и храним в отведённых нам 32-х разрядах.

Ни что не мешает, кроме защищенного режима.


 
korneley ©   (2007-11-06 01:10) [46]


> Ни что не мешает, кроме защищенного режима
Не, ну своей переменной типа TObject, я могу присвоить всё, что угодно (а потом удивляться результатам), не взирая ни на что, тем более на "защищенный режим".


 
Юрий Зотов ©   (2007-11-06 01:14) [47]

> ProgRAMmer Dimonych ©   (06.11.07 00:27) [38]
> Компилятор нагрузить этим делом.

> vpbar ©   (06.11.07 00:28) [39]
> Компилятор добавит нужную информацию.

Вы представляете себе, что это будет за компилятор? Сложность его? Скорость его? Надежность его?

И объект, хранящий адреса всех ссылок на себя - Вы представляете, насколько увеличится объем занимаемой им памяти? Понимаете, что память эту придется еще и постоянно перераспределять (количество ссылок ведь меняется)? Что это будут серьезные тормоза, что увеличится фрагментированность памяти?

И главное - а зачем ве это? Чтобы люди, не понимающие, что такое указатель, тоже могли программировать? А на фига? Планете нужны не только программисты.

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


 
korneley ©   (2007-11-06 01:38) [48]


> Юрий Зотов ©   (06.11.07 01:14) [47]
Как занятно эволюционировала ветка... Начали с использования указателей на объекты, а закончили (?) рекомендациями кому и чем следует заниматься :) Да, всетаки "неблагодарное это занятие - бить красных..." (с)


 
Юрий Зотов ©   (2007-11-06 01:45) [49]

> korneley ©   (06.11.07 01:38) [48]

А что тут странного или плохого? У меня, скажем, нет абсолютно никаких способностей к рисованию - ну так я и в художники не лезу, и обиженным себя нисколько не чувствую. Люди все разные, кому-то быть художником, кому - программистом, кому - певцом. И ничего странного, и ничего плохого.


 
korneley ©   (2007-11-06 02:58) [50]


>  Люди все разные, кому-то быть художником, кому - программистом,
>  кому - певцом
Да, конечно. Но тема [0] исчерпана (до очередного неофита, наступившего туда же). Так не лучше ли завести другую ветку и в другом месте? Например, "Прочее".


 
Германн ©   (2007-11-06 03:07) [51]


> korneley ©   (06.11.07 02:58) [50]
>
>
> >  Люди все разные, кому-то быть художником, кому - программистом,
>
> >  кому - певцом
> Да, конечно. Но тема [0] исчерпана (до очередного неофита,
>  наступившего туда же). Так не лучше ли завести другую ветку
> и в другом месте? Например, "Прочее".
>

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


 
korneley ©   (2007-11-06 03:12) [52]


>  Но, имхо, кроме тебя в сей ветке этот вопрос никого не
> интересует

Дык, понятно, четвертый час пошел... :)


 
homm ©   (2007-11-06 06:50) [53]

> [19] ProgRAMmer Dimonych ©   (05.11.07 23:47)
> Кажется, понял. Тогда получается, что, действительно, без
> присвоения указателю FFile:=nil будет глючить. Так вроде?

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

И это, TFileStream вообще не причем. ([2])


 
homm ©   (2007-11-06 07:01) [54]

> [34] ProgRAMmer Dimonych ©   (06.11.07 00:14)
> > Ну тогда ссылки считать, и не освобождать пока все ссылки
> > не обнулятся.
>
> В COM, кажется, так делается?

В КОЛ тоже так-же. Чесно говоря всю жизнь думал, что и в VCL так-же. Счас поискал подобное, не нашел. Кашмар… Может плохо искал?


 
Anatoly Podgoretsky ©   (2007-11-06 07:51) [55]

> Юрий Зотов  (06.11.2007 00:24:36)  [36]

Тем более если ее не существует.


 
vpbar ©   (2007-11-06 09:33) [56]

>>homm ©   (06.11.07 07:01) [54]
Неее. В KOL не совсем так. Ну не мог Кладов исправить компилятор и заставить вести его подчет ссылок на объект.


 
Юрий Зотов ©   (2007-11-06 09:42) [57]

> vpbar ©   (06.11.07 09:33) [56]

Подсчет ссылок на объект сделать, кстати, можно. И сделано (в той же джаве, скажем). И в Delphi это есть (интерфейсная модель).

Но счетчик ссылок позволит лишь узнать, есть они или нет. И по-прежнему не позволит их обнулить при разрушении объекта. Поскольку адреса самих ссылок неизвестны.


 
vpbar ©   (2007-11-06 09:43) [58]


> >>Юрий Зотов ©   (06.11.07 01:14) [47]



>
> > Вы представляете себе, что это будет за компилятор? Сложность
>
> > его? Скорость его? Надежность его?

Да.  Не сложнее существующих. Такой же простой как, например компилятор Делфи. Скорость не сильно пострадает. Надеженость, конечно зависит от сложности, но не так прямо.

> > И объект, хранящий адреса всех ссылок на себя - Вы представляете,
>
> >  насколько увеличится объем занимаемой им памяти? Понимаете,
>
> >  что память эту придется еще и постоянно перераспределять
>
> > (количество ссылок ведь меняется)? Что это будут серьезные
>
> > тормоза, что увеличится фрагментированность памяти?

Представляю. А сколько может быть максимально ссылок? Ну штук десяток. Больше редко. Так что скажем 320 бит в среднем на объект хватит и перераспределять необязательно. На 64х разрядной платформе поболее будет, но там все побольше.

> > И главное - а зачем ве это?

В данной ветке, просто как пример что альтернатива может быть. А вообще действительно это мало кому нужно. Ибо пошли другим путем. Ссылки считают.


 
homm ©   (2007-11-06 09:43) [59]

> [56] vpbar ©   (06.11.07 09:33)
> В KOL не совсем так.

Не совсем так как в COM, но так, как в [34] написано. О добавлении ссылок, конечно сам программист должен заботится.


 
vpbar ©   (2007-11-06 09:44) [60]

>>Юрий Зотов ©   (06.11.07 09:42) [57]
Нда. Надо сохранить ветку как доказательство моих авторских прав на идею :)


 
vpbar ©   (2007-11-06 09:54) [61]


> homm ©   (06.11.07 09:43) [59]

Вы о чем? Я не увидел в /34/ ничего такого.
В КОЛ просто делается в free
 if @ Self <> nil then  RefDec;
Да объект не удалится пока fRefCount>0
Но увеличение ссылок возложено на программиста, ему нужно  RefInc самому вызывать. Или вы какойто другой механизм в КОЛ имеете ввиду?


 
homm ©   (2007-11-06 09:59) [62]

> [61] vpbar ©   (06.11.07 09:54)
> Я не увидел в /34/ ничего такого.

Сори, в [29].

Ну тогда ссылки считать, и не освобождать пока все ссылки не обнулятся.


 
Юрий Зотов ©   (2007-11-06 10:10) [63]

> vpbar ©   (06.11.07 09:43) [58]

> Скорость не сильно пострадает.
Ой ли?
Пусть имеем a := b; // Обе переменные  - ссылки на объект.
Если не учитывать ссылки, то это одна команда MOVE. А если учитывать, то к этой команде надо еще добавить внесение новой ссылки в список ссылок в самом объекте. А если емкость списка исчерпана, то еще и перераспределить память под него.

И так почти при каждом чихе, потому что операция ОЧЕНЬ частая. Итого можно прогнозировать замедление на один-два порядка. То есть, от 10 до 100 раз.

> Надеженость, конечно зависит от сложности, но не так прямо.
В основном, от сложности и зависит. А от чего ей еще зависеть? Простые вещи разработчиками компиляторов давно отработаны.

> А сколько может быть максимально ссылок?
Сколько угодно. Максимально - сколько хватит памяти.

> Ну штук десяток. Больше редко.
И как поступать разработчикам Вашего компилятора? Сколько памяти отводить под список ссылок? Отвести N байт и надеяться на "авось, больше не потребуется"? Глупо. Менять емкость списка динамически? Тогда на каждом чихе пойдет перераспределение памяти - а это серьезные тормоза и серьезная фрагментация памяти.

>Так что скажем 320 бит в среднем на объект хватит
Размер объекта TFont - 40 байт. При Вашем подходе он становится 360 байт. Разница в 9 раз. Примерно такая же картина и по другим объектам. Итого можно прогнозировать увеличение расхода памяти в 5-10 раз.

> и перераспределять необязательно.
Вот это мило. А как? Отвести под список 320 байт и молиться Богу, чтобы не появилась 11-я ссылка? Класная программа получится.


 
homm ©   (2007-11-06 10:20) [64]

> [63] Юрий Зотов ©   (06.11.07 10:10)
> >Так что скажем 320 бит в среднем на объект хватит
> Размер объекта TFont - 40 байт. При Вашем подходе он становится
> 360 байт.

При его подходе он становится 80 байт или 640 бит. Хотя конечно ы целом с вами сагласен, нафиг это не нужно.
С другой стороны удивляет, почему не был опрганизован элементврный подсчет ссылок, как в KOL. Это же очень удобно.

MyBitmap := TBitmap.Create;
Component1.Bitmap := MyBitmap;
MyBitmap.Free;

function TComponent.SetBitmap(aBitmap: TBitmap);
begin
  fBitmap.Free;
  fBitmap := aBitmap;
  fBitmap.RefInc;
end;

function TComponent.Destroy();
begin
  fBitmap.Free;
end;


 
homm ©   (2007-11-06 10:21) [65]

>  fBitmap.RefInc;

if fBitmap <> nil
 fBitmap.RefInc;


 
Юрий Зотов ©   (2007-11-06 10:33) [66]

> homm ©   (06.11.07 10:20) [64]

1. Верно, спутал. Всего лишь вдвое, оказывается. :o)

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

Поэтому насчет удобства: кому как, а мне удобнее другое - когда я сам рулю программой так, как мне это надо, и никто мне не мешает это делать.


 
homm ©   (2007-11-06 10:40) [67]

> [66] Юрий Зотов ©   (06.11.07 10:33)

Я говорю о подсчете ссылок как таковом, не с точки зрания сабжа. Посмотрите, на пример, там [64] Вы сами рулите кодом. После того, как Вы вызвали MyBitmap.Free; для вас он уже уничтожен (как бы ни было на самом деле) и пользоваться им не должны. А для компонента TComponent он существует, и если бы не вызывать MyBitmap.Free; он будет существовать для обоих кусков кода, помоему все просто и логично, и не нужно беспокоиться о том, что кто-то уничтожит объект. Нужен на каком-то участке — добавил ссылку, не нужен больше — Free, и пофиг, уничтожился ли объект на самом деле, главное что твоей ссылки на него больше нет, как только других ссылок тоже не окажется, уничтожится.


 
vpbar ©   (2007-11-06 10:57) [68]

>>Юрий Зотов ©   (06.11.07 10:10) [63]

> Ой ли?

- так вы про скорость работы?
А я понял из этого
> Вы представляете себе, что это будет за компилятор? Сложность
> его? Скорость его?

что про скорость компилятора. Скорость компилятора не сильно изменится должна. Ведь при присваивании строк тоже не просто мувом обходятся. И ничего.
А вот скорость присваивания выростет - это да. Но тут уже вопрос оптимизации.
>>Сколько угодно. Максимально - сколько хватит памяти
Не спорю. Но с трудом представляю зачем на каждый объект ссылаться из сотни мест сразу.

>  как поступать разработчикам Вашего компилятора?

Примерно так же как поступили разработчики TList. Посмотрите его реализацию. Память там перераспределяетя не на каждый чих.


>
> >Так что скажем 320 бит в среднем на объект хватит
> Размер объекта TFont - 40 байт. При Вашем подходе он становится
> 360 байт. Разница в 9 раз. Примерно такая же картина и по
> другим объектам. Итого можно прогнозировать увеличение расхода
> памяти в 5-10 раз.
>

Эээ. откуда лишние 360-40 = 320 Байт?? Я предлагал 320 Бит т.е. 40 Байт. Да в два раза больше. Но опять же можно оптимизировать.

> > и перераспределять необязательно.

Пока в пределах 32х ссылок не надо перераспределять. Для 33 выделить очередной блок памяти


 
Юрий Зотов ©   (2007-11-06 11:00) [69]

> homm ©   (06.11.07 10:40) [67]

Тут ничего нового, так оно в той же джаве, например, и работает. Плюс там никаких Free вообще нет, а счетчик ссылок рулится их явных присвоением, их явной очисткой и областями их видимости (как длинные строки и динамические массивы в Delphi).

Ну да ладно, вернемся к Delphi (или KOL). Пусть работает подсчет ссылок и мы имеем вот что:

type
 TMyObject = ...
   FRef: TObject;
   procedure Method1;
   procedure Method2;
 end;

procedure TMyObject.Method1;
begin
 FRef := TSomeObject.Create(...);
 ... // Что-то делаем
 if SomeCondition then
   FRef.Free;
end;

procedure TMyObject.Method2;
begin
 ... // Вопрос на засыпку - как мне в этом месте кода узнать,
 ... // существует ли объект, на который ссылается поле FRef,
 ... // или он уже уничтожен? Конечно, учитывая, что в других
 ... // местах программы тоже могут ссылки на этот же объект

end;


 
Юрий Зотов ©   (2007-11-06 11:02) [70]

> vpbar ©   (06.11.07 10:57) [68]

С Вашего разрешения, я эту дискуссию закончу, ладно?


 
homm ©   (2007-11-06 11:03) [71]

> ... // Вопрос на засыпку - как мне в этом месте кода узнать,
> ... // существует ли объект, на который ссылается поле  FRef,
> ... // или он уже уничтожен? Конечно, учитывая, что в других
> ... // местах программы тоже могут ссылки на этот же объект

if not SomeCondition then ???


 
Юрий Зотов ©   (2007-11-06 11:05) [72]

> homm ©   (06.11.07 11:03) [71]

А в Method2 уже нет никаких SomeCondition.


 
homm ©   (2007-11-06 11:09) [73]

> [72] Юрий Зотов ©   (06.11.07 11:05)

Юрий, ну Вы же понимаете что этот пример никакого отношения не имеет к подсчету ссылок, решается точно так-же, как и сабжевая проблема.

Вариант1:
procedure TMyObject.Method1;
if SomeCondition then
  FreeAndNil(FRef);
end;


Вариант2:
procedure TMyObject.Method1;
 if SomeCondition then begin
   FRef.Free;
   SC := TRUE;
 end;
end;


 
homm ©   (2007-11-06 11:10) [74]

> [73] homm ©   (06.11.07 11:09)
> Вариант2:
> procedure TMyObject.Method1;
> if SomeCondition then begin
>   FRef.Free;
>   SC := TRUE;
> end;
> end;

Правильнее так:
procedure TMyObject.Method1;
 SC := SomeCondition;
 if SC then
   FRef.Free;
end


 
vpbar ©   (2007-11-06 11:19) [75]

>>Юрий Зотов ©   (06.11.07 11:02) [70]
Конечно. Собственно хотел тоже предложить Ибо думаю, Вы согласитесь, что непреодолимых технических трудностей нет. Есть недостатки самой технологии.
>>Юрий, ну Вы же понимаете что этот пример никакого отношения не имеет к подсчету ссылок, решается точно так-же, как и сабжевая проблема.
Вообще то имеет. Если бы был действительно подсчет ссылок, то вопрос был бы лишен смыла. Ибо объект существует пока на него хоть кто-нибудь ссылается. Если мы можем обратиться к объекту через ссылку( она не равна nil) то он существует.
Т.е. вопрос можно ли обратиться к объект через какую нить ссылку решается проверкой не равна ли эта ссылка nil.
Если стоит вопрос сущеcтвеует ли объект вообще. То тут только глобальные флаги выставлять при его дестрое, по-моему.


 
Юрий Зотов ©   (2007-11-06 11:21) [76]

> homm ©   (06.11.07 11:09) [73]

В том-то и дело, что имеет. Если посчет ссылок работает, то ЛЮБОЙ вызов Free - это не уничтожение объекта, а всего лишь ЗАПРОС на его уничтожение. И этот запрос не дает никаких гарантий, что объект действительно будет уничтожен именно сейчас, а не через полчаса, другим запросом.

Поэтому при автоматическом уничтожении объекта по обнулению счетчика ссылок, если где-то хоть раз вызвано Free, то нет никаких способов узнать, существует ли еще объект, или он уже уничтожен. Это принципиально.

И Ваши что первый, что второй вариант на этот вопрос тоже не отвечают. Потому что ни FRef=nil, ни SC=TRUE не дают никаких гарантий. Эти условия лишь говорят о том, что метод Free был вызван, и все. А был ли объект при этом уничтожен, или нет (потому что где-то на него висят другие ссылки) - на ЭТОТ вопрос ответа нет.

И быть не может, понимаете?


 
homm ©   (2007-11-06 11:26) [77]

> [76] Юрий Зотов ©   (06.11.07 11:21)
> И Ваши что первый, что второй вариант на этот вопрос тоже
> не отвечают. Потому что ни FRef=nil, ни SC=TRUE не дают
> никаких гарантий. Эти условия лишь говорят о том, что метод
> Free был вызван, и все. А был ли объект при этом уничтожен,
> или нет (потому что где-то на него висят другие ссылки)
> - на ЭТОТ вопрос ответа нет.


Понял теперь о чем Вы.
А для чего это может понадобиться, точно знать что объект убит? Нужели Вы ожидаете некую полезную работу, происходящую в Destroy этого объекта? Помоему тогда логику приложения нужно менять.


 
homm ©   (2007-11-06 11:31) [78]

> [75] vpbar ©   (06.11.07 11:19)
> Вообще то имеет.

Ты путаешь подсчет ссылок и автоматический подсчет ссылок. В механизме подсчета ссылок объект существует пока об обратном не сказано явно вызавом Free. После этого для вызвавшего участка кода (функции, компонента) объект не существует, существует ли он для какого-то другово участка кода не имеет значение.


 
vpbar ©   (2007-11-06 11:42) [79]

homm ©   (06.11.07 11:26) [77]
+5
homm ©   (06.11.07 11:31) [78]


 
Palladin ©   (2007-11-06 11:43) [80]


> homm ©   (06.11.07 11:26) [77]

Да тот же самый TFileStream, если я освобождаю объект, я точно знаю что handle закрыт и уж никак не ожидаю, что он закроется через пол часа...


 
vpbar ©   (2007-11-06 11:44) [81]


> homm ©   (06.11.07 11:31) [78]
> Ты путаешь подсчет ссылок и автоматический подсчет ссылок

Нда. Просто я когда говорю "подсчет ссылок" имею ввиду автоматический. Но вообщем согласен.


 
Palladin ©   (2007-11-06 11:46) [82]

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


 
vpbar ©   (2007-11-06 11:50) [83]

>>Palladin ©   (06.11.07 11:43) [80]
В системах со сборкой мусора (автоматическим потсчетом ссылок)  будет другая логика. Удаление объекта отдельно, а закрытие отдельно. И проблем не будет.
Если несколько ссылок на  TFileStream и вы освободили одну. Остальные продалжают ссылаться и для них TFileStream должен быть валидным. Поэтому закрывать handle нуждно после освобождения всех ссылок. Хотя с другой стороны если вы вызвав TFileStream.free ожидаете что закрытия хендла то такой подход приведет к ошибке. Вообщем фигня получается.
Это наверно тот случай когда моё предложение сохранять все ссылки в объекте имеет какю-то пользу.


 
homm ©   (2007-11-06 11:54) [84]

> [80] Palladin ©   (06.11.07 11:43)

Вполне логично предположить, что если Вы не передавали созданный TFileStream в чужой код, то он уничтожится непосредственно после Free.
Допустим такую логику:

FS := TFileStream.Create("C:\file");
R := TReader.Create;
R.Stream := FS;
R.ReadValue(value);
FS.Free;
// здесь мы не знаем, уничтжен ли FS
R.Free;
// но здесь то точно знаем, что ему кранты.
DeleteFile("C:\file");


Согласен, нужно придерживатся каких-то еше правил, но помему это не слишком обременительно. С другой стороны, с точки зрения VCL этот пример вообще не коректен, т.к. я освободил FS до R?


 
Юрий Зотов ©   (2007-11-06 12:06) [85]

> homm ©   (06.11.07 11:26) [77]

> А для чего это может понадобиться, точно знать что объект убит?

А представьте себе, что Method2 - это деструктор объекта TMyObject. И в этом деструкторе надо освободить ресурсы - то есть, уничтожить объект, на который ссылается FRef. А я не знаю, существует ли еще этот объект, или он уже уничтожен. И у меня нет никаких способов узнать это.

Если в Method2 я просто напишу FRef.Free, то либо все пройдет нормально (объект еще существует), либо получим AV (объект уже был уничтожен и адрес, хранящийся в FRef стал недействительным). Результат совершенно непредсказуем - а самое печальное в том, что перед вызовом FRef.Free у меня нет возможности как-то проверить валидность ссылки FRef.

Принципиальный недостаток здесь в том, что нам дали способ сделать запрос на уничтожение объекта (вызов Free), но не дали способа узнать результат этого запроса. Вот в чем противоречие и в чем весь фокус.

В то же время, когда никакого подсчета ссылок нет и програмой я рулю сам (как в Delphi), то за валидностью ссылок я сам и слежу. И есть полная гарантия, что после вызова Free объект действительно уничтожен. Все просто, все однозначно и никаких проблем.


 
homm ©   (2007-11-06 12:16) [86]

> [85] Юрий Зотов ©   (06.11.07 12:06)
> Если в Method2 я просто напишу FRef.Free, то либо все пройдет
> нормально (объект еще существует), либо получим AV (объект
> уже был уничтожен и адрес, хранящийся в FRef стал недействительным).

[73] Вариант1.

Не понимаю, как вы узнаетет, существует ли объект в приведенном Вами примере, если подсчета ссылок нет.


 
homm ©   (2007-11-06 12:19) [87]

> но не дали способа узнать результат этого запроса.

Обычно этого и не требуется. На пример с TFileStream и хэндлом я ответил.


 
Юрий Зотов ©   (2007-11-06 14:24) [88]

> homm ©   (06.11.07 12:16) [86]

Если подсчета ссылок нет, то FreeAndNil решает все проблемы. После этого вызова объект ТОЧНО уничтожен и об этом ОДНОЗНАЧНО сигнализирует FRef=nil.

А вот если он есть, то путает все карты и узнать ничего нельзя.

> homm ©   (06.11.07 12:19) [87]
> Обычно этого и не требуется.


"Обычно" - это не "всегда". И если все же потребовалось, то как быть?

Пример такого "потребовалось" был приведен. Вопрос был задан. Ответа получено не было. Его и не могло быть получено. Потому что его нет.

==============

Это "удобство" - кажущееся. Потому что лишает программиста возможности самому решать, когда и что нужно делать.

Примерно как автомобиль, который дает водителю возможность крутить руль, но КОГДА начать поворачивать - решает сам. Обычно он решает правильно, но "обычно" - это не "всегда"...


 
Anatoly Podgoretsky ©   (2007-11-06 18:57) [89]

> korneley  (06.11.2007 01:38:48)  [48]

> Начали с использования указателей на объекты, а закончили (?)

Не гони лошадей



Страницы: 1 2 3 вся ветка

Форум: "Начинающим";
Текущий архив: 2007.12.02;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.75 MB
Время: 0.054 c
15-1193841830
@!!ex
2007-10-31 17:43
2007.12.02
Она сдала зачет!


2-1194597655
allucard
2007-11-09 11:40
2007.12.02
Работа с массивом через указатели


15-1193418318
Cerberus
2007-10-26 21:05
2007.12.02
Где могут заменить дисплей на плеере.


2-1194634076
_Levin
2007-11-09 21:47
2007.12.02
Организовать цикл.


15-1194000098
de.
2007-11-02 13:41
2007.12.02
Cursor





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский