Explicitly expressing ownership in Delphi
Asked Answered
M

3

8

I'm primarily a C++ programmer, and I've grown used to having class templates like std::unique_ptr, std::shared_ptr, etc for expressing ownership of my objects. Does Delphi have anything that is similar in its standard library? Are there any best-practices out there for expressing object ownership that I should be following as I write my code?

Edit: Since C++11 became standard, there are two lightweight helper classes, std::shared_ptr and std::unique_ptr.

If I create a variable of type std::shared_ptr<int>, it represents a pointer to an int with shared ownership: under the hood is reference-counted, and when the ref-count reaches zero then the pointer is automatically freed. This type expresses a kind of "shared ownership", where many objects share the responsibility of destroying the resource when they are done with it.

In contrast, std::unique_ptr expresses single ownership. When the unique_ptr goes out of scope, the resource is automatically freed. std::unique_ptr cannot be copied: there can be exactly one object owning this resource at a time, and there is exactly one object who is responsible to clean the object up.

Contrast these lightweight classes with a naked pointer to int, where it can represent either shared ownership, unique ownership, or it can just be a reference to an object somewhere else! The type tells you nothing.

My question is: as Delphi supports holding references to objects, are there any mechanisms for explicitly stating "I am the sole owner of this object, when I'm done with it, I will free it", vs "I am merely keeping a reference to this object around for the purpose of interacting with it, but somebody else will clean it up" vs "I share this object with many other objects, and whoever has it last gets to clean it up."

I know that Collections.Generics has different collections such as TList vs TObjectList, where TObjectList will free the members stored within it, but TList won't. You can say that TObjectList "owns" it's elements, whereas TList doesn't. This is the essence of my question, really. When designing my own classes, are there ways of directly expressing these kinds of ownership issues within the language? Or are there any best practices/naming conventions that are common amongst developers?

Mukul answered 12/9, 2013 at 12:43 Comment(8)
I don´t know what those templates do, so it´s hard to compare their functionality to any other present in Delphi. Maybe you could give some more details on what behavior you would like to get from Delphi. Also, include a tag for the specific Delphi versions you are interested to, so the answers can be better addressed.Colner
Thanks, I added some explanation of the classes, and fleshed out my question further.Mukul
You mean std::shared_ptr<int>, right? I guess the brackets got mangled by the markup language.Colo
Yes that's correct. The markup ate my template brackets.Mukul
Something akin to std::shared_ptr you can do with generic interfaces. But not std::unique_ptr. There's nothing in Delphi that allows you to block assignment.Palatine
I figured this would be the case, as as far as I know C++ is the only mainstream language that supports creating movable but non-copyable objects. Are there any best practices (naming conventions, etc) that Delphi programmers use to express these kinds of relationships? Or is it mostly just left to the developers to "get it right"?Mukul
Maybe have a look here : #781600 or perhaps : blog.barrkel.com/2008/09/smart-pointers-in-delphi.htmlRoma
Thanks, I'll have a look at those resources.Mukul
G
4

I am not aware of any language constructs that can help, nor of any "standard naming conventions".

However, long ago, I have adopted the following naming convention to make it easier to check whether classes clean up behind themselves properly:

  • As per the standard Delphi convention all field names start with an "F".
  • Object references for which the class has / takes on life time management responsibility, start with "FMy".
  • Interface references the class should release explicitly, by setting the reference to nil in the destructor (for performance, to break a cyclic dependency, etc.) start with "FMi"

Very crude, but it works, and has helped a lot when going through code you haven't seen in a while to prevent those "Wait, shouldn't that reference be freed or nilled?" searches.

Glace answered 12/9, 2013 at 18:7 Comment(1)
This is a good idea. Thanks. I already follow the whole F prefix thing, but my codebase is old and crazy! Maybe when I have to refactor some parts I'll start enforcing a naming convention like yours.Mukul
D
2

std::unique_ptr cannot be copied: there can be exactly one object owning this resource at a time

In the Delphi language, there is no type nor mechanism that prevents to share 'ownership'. A copy of any reference can always be made. (Read: there's nothing in Delphi that allows you to block assignment, as David nicely put it.)

When the unique_ptr goes out of scope, the resource is automatically freed.

In Delphi, this is only possible with (or via) interfaces. Delphi has no garbage collector.

And there is exactly one object who is responsible to clean the object up.

Responsibility for cleaning up you have to enforce by yourself. Or delegate that task to a(nother) framework. For example, the default Delphi VCL class TComponent implements automatic ownership (and destruction) that can optionally be exchanged/controlled with RemoveComponent and InsertComponent.

When designing my own classes, are there ways of directly expressing these kinds of ownership issues within the language? Or are there any best practices/naming conventions that are common amongst developers?

Not exactly on topic, but certainly related: there are multiple 'singleton' design pattern implementations that enforce single-time creation of objects.

Regarding naming conventions: the term "Owner" (or "OwnsObjects" from your own example) definitely expresses ownership in the sense that that owner will take care of destruction when necessary. Thus a button created with a form as owner (the single parameter of the button's default constructor) needs no manual destruction.

Dispraise answered 12/9, 2013 at 18:1 Comment(2)
Note that the mobile compilers for Delphi >= XE4 implement automatic reference counting: docwiki.embarcadero.com/RADStudio/XE4/en/…Gargantuan
Thanks for this. My motivation is that I have many times where I hold a reference to another object where I'm merely "looking" at it, and should not destroy it in the destructor, because it's owned by another object in my program that will eventually call free on it. I guess it will have to come down to naming convention then.Mukul
C
1

The concepts in Delphi differ from C++ in many occasions. Both languages are third generation, but Delphi likes to work on a higher level of abstraction than C++. For instance, Delphi supports pointers but they are rarely used when campared to the concept of reference, which is not precisely the same one as in C++.

In Delphi, object variables are in fact references (or in a lower level of abstraction, they are pointers). In C++, when you declare an object variable, the constructor is invokated imediatly, in Delphi it´s not and you have to call it in a given moment, what will allocate memory and run the constructor. So, the memory management of objects in C++ and Delphi are conditionated to different life cycles.

All this was said just to tell you that the memory management design style in Delphi is different than C++. That´s why Delphi doesn´t have any helper class that does precisely what you want. However, Delphi provides a concept named Interfaces, that do not exist in C++ (at least, it didn´t when I used to work with C++, ages ago). Interfaces are similar to an abstract class in the sense they do not have code. You have to provide a class implementor to a interface and that class will provide the code. However, Interfaces provide a reference-count memory management that, I believe, is close to what your are looking for.

So, my answer to you is: the closest language construct that Delphi has to offer you in terms of memory management that can be used to your purposes is Interfaces. So, I suggest that you study it at least a bit to get your own conclusions.

Colner answered 12/9, 2013 at 18:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.