开发者

What does "free" do in Delphi?

I found the following code snippet here:

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

Just curious, what does the free statement/function (between finally开发者_StackOverflow社区 and end) do here? Google did not help.


The code

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

is shorthand for

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

TClipper.Create creates an object of type TClipper, and returns this, and the with statement, which works as in most languages, lets you access the methods and properties of this TClipper object without using the NameOfObject.MethodOrProperty syntax.

(A simpler example:

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

can be simplified to

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

)

But in your case, you never need to declare a TClipper object as a variable, because you create it and can access its methods and properties by means of the with construct.

So your code is almost equivelant to

var
  Clipper: TClipper;

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

The first line, Clipper := TClipper.Create, creates a TClipper object. The following three lines work with this object, and then Clipper.Free destroys the object, freeing RAM and possibly also CPU time and OS resources, used by the TClipper object.

But the above code is not good, because if an error occurrs (an exception is created) within AddPolygon or Execute, then the Clipper.Free will never be called, and so you have a memory leak. To prevent this, Delphi uses the try...finally...end construct:

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

The code between finally and end is guaranteed to run, even if an exception is created, and even if you call Exit, between try and finally.

What Mason means is that sometimes the with construct can be a paint in the ... brain, because of identifier conflicts. For instance, consider

MyObject.Caption := 'My test';

If you write this inside a with construct, i.e. if you write

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

then you might get confused. Indeed, most often Caption := changes the caption of the current form, but now, due to the with statement, it will change the caption of MyObject instead.

Even worse, if

MyObject.Title := 'My test';

and MyObject has no Caption property, and you forget this (and think that the property is called Caption), then

MyObject.Caption := 'My test';

will not even compile, whereas

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

will compile just fine, but it won't do what you expect.

In addition, constructs like

with MyObj1, MyObj2, ..., MyObjN do

or nested with statements as in

with MyConverter do
  with MyOptionsDialog do
    with MyConverterExtension do
      ..

can produce a lot of conflicts.

In Defence of The With Statement

I notice that there almost is a consensus (at least in this thread) that the with statement is more evil than good. Although I am aware of the potential confusion, and have fallen for it a couple of times, I cannot agree. Careful use of the with statement can make the code look much prettier. And this lessens the risk of confusion due to "barfcode".

For example:

Compare

var
  verdata: TVerInfo;

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

with

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

There is absolutely no risk of confusion, and not only do we save a temporaray variable in the last case - it also is far more readable.

Or what about this very, very, standard code:

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

Exactly where is the risk of confusion? From my own code I could give hundreds of more examples of with statements, all simplifying code.

Furthermore, as have been stated above, there is no risk of using with at all, as long as you know what you are doing. But what if you want to use a with statement together with the MyObject in the example above: then, inside the with statement, Caption is equal to MyObject.Caption. How do you change the caption of the form, then? Simple!

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

Another place where with can be useful is when working with a property or function result that takes a non-trivial amount of time to execute.

To work with the TClipper example above, suppose that you have a list of TClipper objects with a slow method that returns the clipper for a particular TabSheet.

Ideally you should only call this getter once, so you can either use an explicit local variable, or an implicit one using with.

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

OR

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

In a case like this, either method would do, but in some circumstances, typically in complex conditionals a with can be clearer.

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

OR

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

In the end is is matter of personal taste. I generally will only use a with with a very tight scope, and never nest them. Used this way they are a useful tool to reduce barfcode.


It's a call to TObject.Free, which is basically defined as:

if self <> nil then
  self.Destroy;

It's being executed on the unnamed TClipper object created in the with statement.

This is a very good example of why you shouldn't use with. It tends to make the code harder to read.


Free calls the destructor of the object, and releases the memory occupied by the instance of the object.


I don't know anything about Delphi but I would assume that it is releasing the resources used by TClipper much like a using statement in C#. That is just a guess....


Any dinamicly created object must call free to free at object creation alocated memory after use. TClipper object is a desktop content creation, capture and management tool. So it is some kind of Delphi connection object with Clipper. The create (object creation) is handled in try finaly end; statment what mean, if connection with Clipper isn't successful the object TClipper will not be created and can not be freed after after of try finaly end; statement.


If "with" is as evil as some posters are suggesting, could they please explain
1. why Borland created this language construct, and
2. why they (Borland/Embarcadero/CodeGear) use it extensively in their own code?
While I certainly understand that some Delphi programmers don't like "with", and while acknowledging that some users abuse it, I think it's silly to say "you shouldn't use it".
angusj - author of the offending code :)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜