• Blog
  • Reflection on mistakes made in object creation in Delphi

Reflection on mistakes made in object creation in Delphi

How to create and destroy objects. Outcomes of the object deletion

Publish date:
Discover more of what matters to you

It is quite obvious that practically every developer, even a newcomer in programming, can create and destroy objects correctly. A classic construction that is used in the majority of programs looks the following way:

123456
MyObject:= TMyClass.Create();
try
{some code…}
finally
MyObject.Free;
end;

Yes, some time ago, there were a lot of discussions about a place of object creation: before an exception handler or within the try-finally-end construction. Of course, now, it is not difficult to find an answer to this question. An example of the well-written code can be found on many online resources, such as stackoverflow, in books and in official documentation.

But do you really understand why it is so and what can happen if you use the wrong variant? It is possible that you will have to answer this question during a technical interview. If you do not know the correct answer, just keep reading this article. So, let’s start.

For our convenience we will use the CodeSite logging system. You can install its lite version from GetIt Package Manager or you can get debug messages another way.

Let’s create a class.

12345678910111213141516171819202122232425262728293031323334353637
TMyClass = class
private
function getSelfPointer: Pointer;
public
constructor Create(beRaized: Boolean = False);
procedure Free; overload;
end;
implementation
{$R *.dfm}
{ TMyClass }
constructor TMyClass.Create(beRaized: Boolean);
begin
if beRaized then
raise Exception.Create('Error Message');
end;
procedure TMyClass.Free;
begin
// inherited;
CodeSite.Send( 'Destroy object');
{$IFNDEF AUTOREFCOUNT}
if Self <> nil then
begin
CodeSite.Send( ' Destroyed object address:', IntToHex(Integer(Pointer(self))));
Destroy;
end;
{$ENDIF}
end;
function TMyClass.getSelfPointer: Pointer;
begin
Result := Pointer(Self);
end;

In our class we will redefine the Free method only for having a look at the address of the object that we want to destroy. We will copy the code that will destroy the object from the Free method of the TObject class. The only unique method will be getSelfPointer that returns the Pointer to the Object.

The class constructor as a parameter will have a logical meaning. If it equals True, an exception will be generated and an object won’t be created.

Now, let’s take two global variables of the TMyClass type.

12345
var
fMain: TfMain;
MyObject,
MyObject2: TMyClass;

Then let’s place two buttons on the form and define click handlers for them.

123456789101112131415161718192021222324252627282930313233
procedure TfMain.Button1Click(Sender: TObject);
begin
MyObject:= TMyClass.Create();
try
CodeSite.Send('Actual MyObject address: ', IntToHex(Integer(MyObject.getSelfPointer ), 8));
finally
MyObject.Free;
end;
MyObject2:= TMyClass.Create();
try
CodeSite.Send('MyObject2 was created. Actual MyObject2 address: ', IntToHex(Integer(MyObject2.getSelfPointer ), 8));
// MyObject:= TMyClass.Create(True);
try
CodeSite.Send('Try creating MyObject');
MyObject:= TMyClass.Create(True);
finally
CodeSite.Send( 'MyObject. Finally');
MyObject.Free;
end;
finally
end;
end;
procedure TfMain.Button2Click(Sender: TObject);
begin
CodeSite.Send( 'MyObject2.ClassName', MyObject2.ClassName );
end;

To begin with, we create an object MyObject and put its address in the memory to the log and immediately destroy it. Then we create MyObject2, check its address in the log and then create MyObject again. At the same time, we generate an exception in the constructor. Actually, this object won’t be created.

According to the official documentation:

If an exception is raised during execution of a constructor that was invoked on a class reference, the Destroy destructor is automatically called to destroy the unfinished object (https://docwiki.embarcadero.com/RADStudio/Sydney/en/Methods_(Delphi))

As a result of the code execution, in the log we see the following:

1234567
Actual MyObject address: = 018A8B80
Destroy object
Destroyed object address: = 018A8B80
MyObject2 was created. Actual MyObject2 address: = 018A8B80
Try creating MyObject.
MyObject. Finally
Destroy object

At the very beginning, MyObject  is created and then it is immediately destroyed. Then MyObject2 is created. And here’s where we have the first surprise. The address of a new object is fully identical to the address of the object that has been destroyed. But, actually, there is nothing unexpected given the peculiarities of how the object storage is organized in Delphi.

Then we need to bear in mind that though the Free method destroys an object, it doesn’t destroy the link that leads to it.

 In other words, MyObject won’t be equal to nil. 

What happens if now during the creation of the object placed inside the exception handler, an error will arise? You are right, the finally block will be executed and the Free method will be called.

We are talking about this part of the code:

123456789
try
CodeSite.Send('Try creating MyObject');
MyObject:= TMyClass.Create(True);
finally
CodeSite.Send( 'MyObject. Finally');
MyObject.Free;
end;

We remember that the object won’t be created. But a static method Free will consider that the object is still available at the address 018A8B80 as the link after the first creation of the object is stored. But in reality that’s the address of another object that will be destroyed. To make sure that it is so, it is enough just to press Button2.

1234
procedure TfMain.Button2Click(Sender: TObject);
begin
CodeSite.Send( 'MyObject2.ClassName', MyObject2.ClassName );
end;

We will get a well-known Access Violation error. 

As a result, due to using this construction for creating and destroying an object, we have destroyed another object.

Similar errors are quite dangerous when it comes to massives (lists) of objects. And the search for such errors is a rather challenging process.

It is obvious that nothing of this kind happens if we create MyObject for the second time in a way that is applied in the majority of cases.

12345678
MyObject:= TMyClass.Create(True);
try
CodeSite.Send('Try creating MyObject');
finally
CodeSite.Send( 'MyObject. Finally');
MyObject.Free;
end;

For a deeper understanding of the information, I recommend reading the following article http://rvelthuis.de/articles/articles-pointers.html, as well as the materials provided by Aleksandr Alekseev https://www.gunsmoker.ru/2009/04/freeandnil-free.html.

Subscribe to our newsletter and get amazing content right in your inbox.

This field is required
This field is required Invalid email address

Thank you for subscribing!
See you soon... in your inbox!

confirm your subscription, make sure to check your promotions/spam folder

Tags

Subscribe to our newsletter and get amazing content right in your inbox.

You can unsubscribe from the newsletter at any time

This field is required
This field is required Invalid email address

You're almost there...

A confirmation was sent to your email

confirm your subscription, make sure to check
your promotions/spam folder