What makes "free" 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 

Curious what the free / function statement (between finally and end ) does here? Google did not help.

+4
source share
6 answers

The code

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

is short 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 like in most languages, allows you to access the methods and properties of this TClipper object without using the NameOfObject.MethodOrProperty syntax.

(Simple 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 using the with construct.

So your code is almost equivalent

 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 next three lines work with this object, and then Clipper.Free destroys the object, freeing up RAM and, possibly, processor time and OS resources used by the TClipper object.

But the above code is not good, because if an error occurs (an exception is thrown) inside AddPolygon or Execute , then Clipper.Free will never be called, and therefore you will 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; 

Code execution is guaranteed between finally and end , even if an exception is thrown, and even if you call Exit , between try and finally .

What Mason means is that sometimes the with construct can be paint in ... the brain due to identity conflicts. For example, consider

 MyObject.Caption := 'My test'; 

If you write this inside the 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 can get confused. In fact, most often, Caption := changes the title of the current form, but now the MyObject title will be changed instead with statement.

Worse, if

 MyObject.Title := 'My test'; 

and MyObject does not have a Caption property, and you forget it (and think the property is called Caption ), then

 MyObject.Caption := 'My test'; 

doesn't 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, designs such as

 with MyObj1, MyObj2, ..., MyObjN do 

or nested expressions with , as in

 with MyConverter do with MyOptionsDialog do with MyConverterExtension do .. 

can cause a lot of conflict.

In defense of with instructions

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

For instance:

Comparison

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

from

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

There is absolutely no risk of confusion, and we not only store the variable temporaray in the latter case - it is also 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 examples of with statements, all simplifying the code.

In addition, as stated above, there is no risk of using with at all if you know what you are doing. But what if you want to use the with statement with MyObject in the example above: then inside the with Caption statement is MyObject.Caption . How do you change the title of a form? Plain!

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

Another place where it can be useful is to work with the result of a property or function, which requires a non-trivial amount of time to execute.

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

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

 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, any method will do, but in some cases, usually under difficult conditions a c may 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, it is a matter of personal taste. I usually use only one with a very narrow area and never nest them. Used in this way, they are a useful tool for reducing barfcode.

+23
source

This is a TObject.Free call, which is basically defined as:

 if self <> nil then self.Destroy; 

Runs on an unnamed TClipper object created in a with statement.

This is a very good example of why you should not use with . This makes the code more difficult to read.

+15
source

Free call to the object's destructor and freeing up memory occupied by the object's instance.

+7
source

I don't know anything about Delphi, but I would suggest that it frees up the resources used by TClipper, like the using statement in C #. This is just a hunch ....

+3
source

Any dynamically created object must be called free of charge in order to free allocated memory after use when creating the object. The TClipper object is a tool for creating, capturing and managing content on the desktop. So this is some kind of object connecting Delphi with Clipper. create (object creation) is processed in the try finaly end , which means that if the connection to Clipper is not performed, the TClipper object will not be created and cannot be freed after the try final end is completed .

0
source

If "c" is as vicious as some posters suggest, could they please 1. why Borland created this language construct and
2. Why do they (Borland / Embarcadero / CodeGear) widely use it in their code?
Although I, of course, understand that some Delphi programmers do not like the "c", and, recognizing that some users abuse it, I think it's silly to say "you should not use it."
angusj - the author of the broken code :)

0
source

Source: https://habr.com/ru/post/1314224/


All Articles