Что делает «бесплатный» в Delphi? - PullRequest
4 голосов
/ 29 июня 2010

Я нашел следующий фрагмент кода здесь :

with TClipper.Create do
  try
    AddPolygon(subject, ptSubject);
    AddPolygon(clip, ptClip);
    Execute(ctIntersection, solution);
  finally
    free;
  end

Просто любопытно, что здесь делает оператор / функция free (между finally и end)?Гугл не помог.

Ответы [ 6 ]

23 голосов
/ 29 июня 2010

Код

with TClipper.Create do
  try
    AddPolygon(subject, ptSubject);
    AddPolygon(clip, ptClip);
    Execute(ctIntersection, solution);
  finally
    free;
  end

является сокращением для

with TClipper.Create do
begin
  try
    AddPolygon(subject, ptSubject);
    AddPolygon(clip, ptClip);
    Execute(ctIntersection, solution);
  finally
    free;
  end;
end;

TClipper.Create, создает объект типа TClipper и возвращает его, а также оператор with, которыйработает как в большинстве языков, позволяет получить доступ к методам и свойствам этого объекта TClipper без использования синтаксиса NameOfObject.MethodOrProperty.

(Более простой пример:

MyPoint.X := 0;
MyPoint.Y := 0;
MyPoint.Z := 0;
MyPoint.IsSet := true;

можно упростить до

with MyPoint do
begin
  X := 0;
  Y := 0;
  Z := 0;
  IsSet := true;
end;

)

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

Таким образом, ваш код почти эквивалентен

var
  Clipper: TClipper;

Clipper := TClipper.Create;
Clipper.AddPolygon(subject, ptSubject);
Clipper.AddPolygon(clip, ptClip);
Clipper.Execute(ctIntersection, solution);
Clipper.Free;

Первая строка, Clipper := TClipper.Create, создает объект TClipper.Следующие три строки работают с этим объектом, а затем Clipper.Free уничтожает объект, освобождая ОЗУ и, возможно, также процессорное время и ресурсы ОС, используемые объектом TClipper.

Но приведенный выше код не годитсяпотому что, если происходит ошибка (создается исключение) в пределах AddPolygon или Execute, то Clipper.Free никогда не будет вызываться, и поэтому у вас есть утечка памяти.Чтобы предотвратить это, Delphi использует конструкцию try...finally...end:

Clipper := TClipper.Create;
try
  Clipper.AddPolygon(subject, ptSubject);
  Clipper.AddPolygon(clip, ptClip);
  Clipper.Execute(ctIntersection, solution);
finally
  Clipper.Free;
end;

Код между finally и end гарантированно будет выполняться, даже если создается исключение и даже если вы вызываете Exit, между try и finally.

Что означает Мейсон, так это то, что иногда конструкция with может быть краской в ​​... мозгу из-за конфликтов идентификаторов.Например, рассмотрим

MyObject.Caption := 'My test';

Если вы напишите это внутри конструкции with, т.е. если вы напишите

with MyObect do
begin
  // A lot of code
  Caption := 'My test';
  // A lot of code
end;

, вы можете запутаться.Действительно, чаще всего Caption := меняет заголовок текущей формы, но теперь, благодаря выражению with, вместо этого будет меняться заголовок MyObject.

Еще хуже, если

MyObject.Title := 'My test';

и MyObject не имеет свойства Caption, и вы об этом забываете (и думаете, что свойство называется Caption), тогда

MyObject.Caption := 'My test';

даже не скомпилируется, тогда как

with MyObect do
begin
  // A lot of code
  Caption := 'My test';
  // A lot of code
end;

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

Кроме того, такие конструкции, как

with MyObj1, MyObj2, ..., MyObjN do

или вложенные with операторы, как в

with MyConverter do
  with MyOptionsDialog do
    with MyConverterExtension do
      ..

может привести к множеству конфликтов.

В защиту заявления With

Я заметил, что почти есть консенсус (по крайней мере, в этой теме), чтоwith утверждение скорее зло, чем добро.Хотя я осознаю потенциальную путаницу и влюбился в нее пару раз, я не могу согласиться.Тщательное использование оператора with может сделать код более красивым.И это уменьшает риск путаницы из-за "штрих-кода" .

Например:

Сравните

var
  verdata: TVerInfo;

verdata := GetFileVerNumbers(FileName);
result := IntToStr(verdata.vMajor) + '.' + IntToStr(verdata.vMinor) + '.' + IntToStr(verdata.vRelease) + '.' + IntToStr(verdata.vBuild);

с

with GetFileVerNumbers(FileName) do
  result := IntToStr(vMajor) + '.' + IntToStr(vMinor) + '.' + IntToStr(vRelease) + '.' + IntToStr(vBuild);

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

Или как насчет этого очень, очень стандартного кода:

with TAboutDlg.Create(self) do
  try
    ShowModal;
  finally
    Free;
  end;

Где именно риск путаницы?Из моего собственного кода я мог бы привести сотни других примеров операторов with, которые все упрощают код.

Кроме того, как уже было сказано выше, риск использования with вообще отсутствует, посколькуты знаешь что делаешьНо что, если вы хотите использовать оператор with вместе с MyObject в приведенном выше примере: тогда внутри оператора with Caption равен MyObject.Caption.Как вы измените заголовок формы?Простой!

with MyObject do
begin
  Caption := 'This is the caption of MyObject.';
  Self.Caption := 'This is the caption of Form1 (say).';
end;

Еще одно место, где может быть полезно с, - это работа со свойством или результатом функции, выполнение которого занимает не тривиальное время.

Работа с TClipperПример выше, предположим, что у вас есть список объектов TClipper с slow методом, который возвращает ограничитель для определенного TabSheet.

В идеале вы должны вызывать этот метод только один раз, так что вы можетеиспользовать явную локальную переменную или неявную, используя с .

var
  Clipper : TClipper;
begin
  Clipper := ClipList.GetClipperForTab(TabSheet);
  Clipper.AddPolygon(subject, ptSubject);
  Clipper.AddPolygon(clip, ptClip);
  Clipper.Execute(ctIntersection, solution);
end;

ИЛИ

begin
  with ClipList.GetClipperForTab(TabSheet)do
  begin
    AddPolygon(subject, ptSubject);
    AddPolygon(clip, ptClip);
    Execute(ctIntersection, solution);
  end;
end;

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

var
  Clipper : TClipper;
begin
  Clipper := ClipList.GetClipperForTab(TabSheet);
  if (Clipper.X = 0) and (Clipper.Height = 0) and .... then
    Clipper.AddPolygon(subject, ptSubject);
end;

ИЛИ

begin
  with ClipList.GetClipperForTab(TabSheet) do
    if (X = 0) and (Height = 0) and .... then
      AddPolygon(subject, ptSubject);
end;

В конце концов, это вопрос личного вкуса.Обычно я использую с только с очень узким прицелом и никогда не вкладываю их.Используемые таким образом они являются полезным инструментом для уменьшения штрих-кода .

15 голосов
/ 29 июня 2010

Это вызов TObject.Free, который в основном определяется как:

if self <> nil then
  self.Destroy;

Он выполняется на безымянном объекте TClipper, созданном в операторе with.

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

7 голосов
/ 29 июня 2010

Free вызывает деструктор объекта и освобождает память, занятую экземпляром объекта.

3 голосов
/ 29 июня 2010

Я ничего не знаю о Delphi, но я предполагаю, что он высвобождает ресурсы, используемые TClipper, так же, как оператор using в C #. Это всего лишь предположение ....

0 голосов
/ 30 июня 2010

Если «с» является таким же злом, как предполагают некоторые авторы, могут ли они объяснить
1. почему Borland создал эту языковую конструкцию, и
2. почему они (Borland / Embarcadero / CodeGear) широко ее используютв собственном коде?
Хотя я, конечно, понимаю, что некоторым программистам на Delphi не нравится «с», и, признавая, что некоторые пользователи злоупотребляют этим, я думаю, что глупо говорить «вы не должны его использовать».
angusj - автороскорбительный код:)

0 голосов
/ 29 июня 2010

Любой динамически созданный объект должен вызвать free , чтобы освободить выделенную память для создания объекта после использования. Объект TClipper - это инструмент для создания, захвата и управления настольным контентом. Так что это какой-то объект соединения Delphi с Clipper. create (создание объекта) обрабатывается в try final end; statment что означает, что если соединение с Clipper не будет успешным, объект TClipper не будет создан и не может быть освобожден после после попробуйте окончательно завершить; оператор.

...