Модераторы: Poseidon, Snowy, bems, MetalFan
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Помогите разобраться в "истоках", try/except/finally + free 
:(
    Опции темы
кварк
Дата 20.1.2005, 12:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 91
Регистрация: 2.8.2002

Репутация: нет
Всего: нет



А почему нет конструкции Try...except...finally?

И как бы ее реализовать? Во многих случаях это нужно.

Ну например, создаем файл:
Код

var
 tfs: TFileStream;
begin
 try
   tfs := TFileStream.Create('C:\1.tmp', fmCreate);
   // Наши действия
 finally
   tfs.Free;
 end;
end;


Там, где действия, могут возникнуть исключения (ну, например, места на диске не хватит). Надо бы их перехватить. Мне пришло на ум следующее:
Код

var
 tfs: TFileStream;
 tfsCreated: Boolean;
begin
 tfsCreated := true;
 try
   try
     tfs := TFileStream.Create('C:\1.tmp', fmCreate);
   except
     tfsCreated := false;
   end;
   if tfsCreated then
   try
     // Наши действия
   except
     // Перехватываем возможные ошибки
   end
 finally
   if tfsCreated then tfs.Free;
 end;
end;


Можно ли упростить эту конструкцию? Без потери функциональности, конечно. А то она выглядит ужасно громоздкой.

Отдельный вопрос по переменной tfsCreated: нигде не встречал, что операция Create обязана возвращать nil при ошибке создания объекта. Просто пару раз сталкивался с ситуацией, что указатель не nil, а тем не менее, объект не создается -> результат любого обращения предсказуем. С тех пор полюзуюсь такой "фигней". Может, как-нибудь можно проверить по объекту создан ли он?

И еще большая странность: процедура Free не устанавливает указатель в nil. На мой взгляд, весьма странное поведение - кому нужен несуществующий указатель, Assigned() от которого возвращает true? IMHO случаев, когда эта ситуация вредна гораздо больше, нежели тех, где она может принести пользу (вообще сомневаюсь в их наличии, разве что экзотика какая). Про FreeAndNil я знаю, но это же лишний вызов функции...

PM MAIL   Вверх
Петрович
Дата 20.1.2005, 14:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1000
Регистрация: 2.12.2003
Где: Москва

Репутация: 25
Всего: 55



Цитата
Можно ли упростить эту конструкцию? Без потери функциональности, конечно. А то она выглядит ужасно громоздкой.

Естественно. Но тут как говорится на вкус и цвет...:
Классический вариант:
Код

var
tfs: TFileStream;
begin
try
  tfs := TFileStream.Create('C:\1.tmp', fmCreate);
  try
    // Наши действия при успешном создании файла
  finally
    tfs.Free; // закрываем созданный файл
  end
except
    // Обрабатываем возможные ошибки создания и записи файла
    raise; // это если мы хотим "распространить" ошибку выше, для того что бы ее могли обработать на более верхних уровнях
end;
end;

В этом варианте не различают обработку ошибок создания, и записи файла.

Лично я, обычно предпочитаю например что-то подобное:
Код

var
tfs: TFileStream;
begin
 try
   tfs := TFileStream.Create('C:\1.tmp', fmCreate);
   try
     // Здесь я пишу в файл
   finally
     tfs.Free; // закрываем созданный файл
   end
 except on e :Excepton do begin
   e.Message := 'Ошибка записи файла  "C:\1.tmp": ' + e.Message;
   raise; // Реакция делается выше. Например, обработка по умолчанию - вывод сообщения на экран
   end;
 end;
end;

На первый взляд сложно, но зато информативно. Хотя в каждом конкретном случае, возможны варианты.

Цитата
Отдельный вопрос по переменной tfsCreated: нигде не встречал, что операция Create обязана возвращать nil при ошибке создания объекта.

Дык не должна, и естественно не возвращает. В случае ошибок, tfs.Created возбуждает исключение и соответственно ничего возвратить в принципе не может.

Цитата
И еще большая странность: процедура Free не устанавливает указатель в nil.

А Free, это не процедура. Это метод объекта, на конкретный экземпляр которого, указывает некая переменная о которой он ничего не знает, и поэтому не в состоянии ее изменить.

Цитата
кому нужен несуществующий указатель

Абсолютно верно, никому.

Цитата
IMHO случаев, когда эта ситуация вредна гораздо больше, нежели тех, где она может принести пользу (вообще сомневаюсь в их наличии, разве что экзотика какая).

Так точно. Пользу она вообще принести не может - только вред.

Цитата
Про FreeAndNil я знаю, но это же лишний вызов функции...

Ну, ты хоченш что-бы все сделалось само? smile На самом деле, это вполне правильный способ уничтожения объекта, по эффективности мало отличающийся от .Free.
Правда в моих примерах он без надобности. В них, обращение к уже уничтоженному экземпляру по указателю tfs в принципе не возможно.


--------------------
Все знать невозможно, но хочется
PM ICQ   Вверх
кварк
Дата 20.1.2005, 15:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 91
Регистрация: 2.8.2002

Репутация: нет
Всего: нет



Спасибо за такой развернутый ответ.

Действительно, классический вариант гораздо красивше.

Цитата
А Free, это не процедура. ...
Согласен. Конечно это метод класса. (Я это и имел в виду). А возвращать она, конечно, ничего не должна. Но теоретически она в случае успеха последним действием могла бы установить self в nil. (Практически это зависит от реализации компилятора, и в Delphi, насколько мне известно, этого не делается.)

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


PM MAIL   Вверх
Петрович
Дата 20.1.2005, 22:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1000
Регистрация: 2.12.2003
Где: Москва

Репутация: 25
Всего: 55



Цитата
Но теоретически она в случае успеха последним действием могла бы установить self в nil. (Практически это зависит от реализации компилятора, и в Delphi, насколько мне известно, этого не делается.)

Ну, тогда компилятор должен особым образом генерить код вызова метода Free. Получится что все методы равны, но некоторые ровнее. А мы знаем к чему это приводит, достаточно взлянуть на нашу страну, или на процедуры Read, ReadLn, Write, WriteLn.
Кроме того, это все равно не решит всех проблем. Вот например:
Код

var
 o1, o2 :tStringList;
begin
 o1 := tStringList.Create;
 o1.Add('Просто строка');
 o2 := o1;
 FreeAndNil(o1); // это то что должен по твоему делать компилятор когда ты пишешь o1.Free;
 // вот здесь, Assigned(o1) = False а вот Assigned(o1) все равно останется True.
 // И от этого защиты нет. По крайней мере без потери эффективности
end;

Цитата
А что, нет быстрых способов узнать: указывает ли данный указатель на корректную область памяти в пределах приложения?

Нет даже медленных smile. Хотя, чудовищно медленный способ пожалуй всетаки наверное есть. Для этого, надо вести учет каждого распределенного объекта, и освобожденного куска памяти. Тогда, в Assigned нужно будет определить какому из списков он принадлежит.
Более простого способа нет. К примеру, во фрагменте приведенном выше, указатель o2 будет указывать на память принадлежащую приложению, но в данный момент не занятую никаким объектом, если кончено приложение не многопоточное.



--------------------
Все знать невозможно, но хочется
PM ICQ   Вверх
кварк
Дата 21.1.2005, 11:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 91
Регистрация: 2.8.2002

Репутация: нет
Всего: нет



Код
Нет даже медленных
Ну это как сказать: перехватывай, например, access violation в критичных местах - вот тебе и метод. Операционка-то сама ведет таблицы распределенной памяти для каждого приложения, зачем дублировать ее работу?

Вот чего я пока не понял - так это того, что в Exception есть поле "текстовое сообщение", а поля "код ошибки" нет. Т.е. в общем случае приходится анализировать текстовую строку. Причем эта строка может быть локализована, поэтому по-хорошему так делать не надо. Приходится писать в столбик "on e:EAccessViolation do", ... "on e:Exception do". При этом надо помнить все типы исключений.

Это сообщение отредактировал(а) кварк - 21.1.2005, 11:38
PM MAIL   Вверх
Петрович
Дата 21.1.2005, 12:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник Клуба
Сообщений: 1000
Регистрация: 2.12.2003
Где: Москва

Репутация: 25
Всего: 55



Цитата
Ну это как сказать: перехватывай, например, access violation в критичных местах - вот тебе и метод.

Не прокатит smile. Не всегда обращение по указателю на уничтоженный объект вызывает AV.
Дело в том, что область памяти которую ранее занимал уничтоженный объект, может быть повторно выделена, уже под другой объект, либо под динамическую переменную (String, DynArray и пр.). Тогда, AV может и не быть (смотря как обращаешся).
Причем, бывает даже так: Был объект, например tStringList. В нем были строки 'Вася', 'Петя', 'Маша'. Указатель на этот объект был в перменной x. Потом этот объект грохнули (x.Free). Через некоторое время вновь создали объект tStringList и разместили указатель на него в y (y := tStringList.Create). Поместили в него строки 'Гриша', 'Миша', 'Сергей'. Потом просто лезем в x.Strings[0] и получаем 'Гриша'!.
А дело в том, что новый объект tStringList "лег" в памыть занимаемую ранее старым tStringList. Поэтому, x вновь стал "действительным".
Можешь поверить, сам с таким сталкивался, правда не у себя в программе smile!


Цитата
Вот чего я пока не понял - так это того, что в Exception есть поле "текстовое сообщение", а поля "код ошибки" нет. ...

Это и правдо печально. smile

Цитата
Приходится писать в столбик "on e:EAccessViolation do", ... "on e:Exception do". При этом надо помнить все типы исключений.

Именно так и приходится. Правда помнить не приходится. Всетаки ООП, наследование, и т.п. не зря придоманны smile.



--------------------
Все знать невозможно, но хочется
PM ICQ   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Общие вопросы"
SnowyMetalFan
bemsPoseidon
Rrader

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0518 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.