In C++ if I define a class that has some members then the class destructor (ie dispose method) will destruct every member in reverse order by default, unless I explicitly write something else. For example, if I define:
class Foo, Bar, Baz;
class MyClass {
MyClass(Foo _foo, Bar _bar, Baz _baz) : foo(_foo), bar(_bar), baz(_baz);
Foo foo;
Bar bar;
Baz baz;
}
Then the destructor for this class will be equivalent to:
MyClass::~MyClass() {
delete baz; // calls Baz::~Baz, the destructor of Baz
delete bar;
delete foo;
// ...destruct superclasses (not applicable here)
}
This is usually what the destructor should look like so it saves a lot of time and avoids memory leaks when you add a member but forget to update the destructor.
In Dart, on this page: https://docs.flutter.dev/cookbook/forms/retrieve-input I see there's an overriden dispose method which looks like the analog of a C++ destructor. This override simply destructs all members and then the superclass.
My question is: does Dart have a facility for implicit destructors like C++ does, or is this dispose boilerplate always necessary when you have class members? If it is required, is it only needed for complex types or are string members etc. automatically disposed?
Related
Given the interface below:
ITest = interface ['guidhere']
procedure TestMethod;
end;
Is there any reason to have TestMethod() declared as public in the implementation class? I have put it in both the private and protected sections and it does not seem to make a difference. I am just wondering if there are any guidelines, from a design perspective (or any perspective really), that make the public section the correct section to implement the method in.
does it matter if the implementing methods are not in the public section?
As far as the compiler is concerned. It makes no difference.
That being said. Private methods will still be private, even if you can access them through the interface.
unit unit1;
....
IItest = interface
['{A3D5FEB6-8E29-4EA8-8DC9-7988294EFA65}']
procedure Test;
end;
TTest = class(TInterfacedObject, IItest)
private
procedure Test;
end;
unit unit2;
....
var
TestT: TTest;
TestI: ITest;
begin
TestT:= TTest.Create;
TestI:= TTest.Create;
TestT.Test; //will not compile.
TestI.Test; //works.
The reason for this is that the interface simply has a list of pointers to methods in its VMT. The definition of the method is given in the interface definition.
The compiler only checks if the signatures match.
It does not check the visibility of the method.
As per Allen's comment this is a deliberate design:
Making the methods private or protected will ensure that you can only access them through the interface. This is a way to enforce a usage contract for the intended use of the object.
Note that this is not an error, or even a bad thing.
Properties can give 'access' to private methods as well:
property Items[index: integer] read GetItem write SetItem;
Here GetItem and SetItem are often private.
This forces you to access the Items using the property.
When using properties the implementing methods are protected (or worse :-) as a rule. The same logic applies to both properties and interfaces.
More so for interfaces, because if you mix interface access and regular access you'll run into reference counting issues.
Clean code
Note that you can have as many visibility sections in a class header as you like.
This way you can put all the interfaced methods in one part and all the non-interfaced methods in another.
TTest = class(TInterfacedObject, I1, I2)
//I1 methods
private
... private I1 methods here...
protected
.. more I1 methods
//I2 methods
private
.. some I2 methods
protected
..more I2 methods
//TTest methods
private
//data members
public
constructor Create;
destructor Destroy; override;
end;
That way it's as clear as can be what's what.
I appreciate this is an old question, but the answer is no longer accurate.
I am using Rio, and from the documentation:
All members of an interface are public. Visibility specifiers and storage specifiers are not allowed. (But an array property can be declared as default.)
which is indeed what I observe. An interface defitnion will not allow protected, private or public to be specified.
Edit: To incorporate comments to this answer
At the time of writing the compiler does not enforce any level of visibility on the methods which implement the interface. The methods will be callable through either the interface (interface reference) or through the class (object instance).
As the interface definition insists that all methods are public, when called through an interface reference the implementing methods are public regardless of how they are defined in the implementing class. As a reference to the interface can be obtained from the object then they are callable from the object, although the caller may have to obtain the interface reference first.
Given that the implementing methods are callable as public through the interface you should consider how helpful it is to not make them public in your class. Restricting the visibility of the methods in the class when they are public in the interface seems to me to be making the code less clear, but other opinions are available!
One of the comments mentioned delegation of the interface. This seems to me to be a distraction as it simply means that when the object retruns the interface reference it returns the reference from the delgated object (which could itself delegate the interface) and ultimately it is the methods of that object whaich are called.
So any class which implements an interface makes the methods which implement the interface available as public when called through the interface.
I have many singletons where I don't want the developers to call the constructor Create. Instead they should use the function Instance
TdgpBankBranch = class(TDataGroup)
strict private
class var InstanceVar : TdgpBankBranch;
private
{ Only for internal use }
constructor Create (AOwner : TComponent); reintroduce;
protected
public
class function Instance : TdgpBankBranch;
class function Is_A_Live_Singleton: boolean;
property Rec[Bank, Branch : integer]: TBankBranch read GetRec; default;
end;
I have been shifting the constructors to private. This makes the code analysers complain "Non-public constructor".
I could shift them back to public. Is there a way to make the compiler fail if an attempt is made at using the constructor ?
Moving the constructor to private will prevent other units from being able to call Create() directly, however code inside the same unit as the class will still be able to call it unhindered, due to implicit friendship within a unit. If you don't want any code outside of the class calling Create() directly, even in the same unit, then declare it as strict private so only the class itself can call it directly.
You ask how to stop the compiler from creating an object when somebody calls .create, but what you're trying to do is create a singleton, so it helps to see how others solved this.
In Delphi, singletons are often implemented via a function that's declared in the interface section and which returns an object that's declared in the implementation section of a unit. The function creates the instance if it doesn't exist yet, to provide lazy loading. Cleanup is done via the finalization section.
In your situation, instead of having an instance() function, you just create a global function that gives you an instance.
This approach probably stems from the days before classes and private sections existed, but it's pretty straightforward, and prevents the issue that you're running in to. Look at how TClipboard is implemented. This implementation is not possible in Java or C# because they don't have global functions, or the type of scoping that makes this Delphi implementation work...
Or take a look at this answer by David Heffernan:
Delphi Singleton Pattern
It's even better than TClipboard, because it hides the entire class in the implementation section, truly preventing people to create instances themselves, but you'll have to create an interface for your class.
The one with virtual/dynamic
// declare in Child Class
constructor Create; virtual;
constructor TChildClass.Create;
begin
inherited;
end;
The one with override.
// declare in Child Class
constructor Create; override;
constructor TChildClass.Create;
begin
inherited;
end;
The one with nothing
// declare in Child Class
constructor Create;
constructor TChildClass.Create;
begin
inherited;
end;
Are these the same thing? It looks confusing.
Yes, there is a difference, but let's deal with the virtual keyword in more basic OOP terms first, yet still how it applies to Delphi methods.
When you declare a derived (child) class, and implement a method as "override", it means that you're overriding (surprise) the matching method of the base class.
This means that you can write code like this:
var child : TBaseClass;
begin
child := TChildClass.Create; // note that it is stored in TBaseClass variable
child.VirtualMethodDefinedInBaseClassThatHasBeenOverriddenInChildClass;
This will call the method in the child class, even if the variable is defined to be of the base class type. This is the whole purpose of virtual methods, you can access the object through a reference of a more general type, and still call methods that have been written for the particular type of object you're dealing with.
If you have a virtual method in the base class that you chose not to override in the child class, but instead reintroduce, you're effectively replacing it in some cases. Note that in most cases you need to tell the compiler that you really meant to do this, though I'm unsure about what Delphi requires here.
Basically, if your variable is of type TBaseClass, and you call a virtual method on it, that has been reintroduced in the TChildClass, it will still call the method in the base class.
However, if your variable is of type TChildClass, and you call that method on it, you will get the new method instead.
Now, for constructors, in Delphi, it is slightly different.
The point of virtual constructors is to be able to virtually construct objects, and to do that, Delphi also has "class types".
You can say this:
type TClassToUse = class of TBaseClass;
var cls : TClassToUse;
obj : TBaseClass;
begin
cls := TChildClass;
obj := cls.Create;
(note that my Delphi knowledge is a bit rusty here, if anyone spots bugs or glaring problems in the above code, please let me know or just fix it)
Here we store a "class" in a variable, and then ask the class to please construct an object for us. This allows us to switch out which class to create, but we also need to declare the constructors we want to use virtual, otherwise we will have problems.
So in the above code, if you declared the constructor as virtual in TBaseClass, and then override it in TChildClass (which the code is actually using in cls), the overridden constructor is the one that will be used.
If, on the other hand, you don't declare the constructor as virtual, we're back to the base class constructor. Virtual basically means to figure out the right method to execute at runtime, whereas non-virtual will figure it out at compile time.
Reintroduction as described for normal methods above, also works this way.
However, virtual constructors are only used as virtual when used through a class type.
No, static and virtual methods are not the same thing.
And override is a case of virtual method.
http://en.wikipedia.org/wiki/Virtual_function
http://docwiki.embarcadero.com/RADStudio/XE4/en/Methods#Method_Binding
Constructors bring nothing special here - they conform to the same rules as other methods for the question
Is it in Delphi (Win32) possible to declare a whole class (not only a function of the class) as static?
Looks like user search for "class functions":
type
TSomeClass = class
class procedure MyFunction(...);
This is like static method, so, call it:
TSomeClass.MyFunction(...);
I assume you mean static classes like in .net (and not "static" as in traditional Delphi/Native) - and the answer to that is no.
I would use an abstract class (not to be confused with an abstract method in a class) to prevent it from being instantiated instead of demoting the constructor to protected:
TMyClass = class abstract
public
class procedure DoStuff; static;
end;
That will enforce the singleton pattern and prevent any instantiations period.
I am not quite sure what you mean by a "static class". You can declare a class, that has only class methods, so these methods can be called without instantiating the class.
TSomeClass.SomeMethod;
Is that what you want?
Not natively.
Depending on what you need it for, if for the purposes of your code, in some use cases you could replace it with a Singleton Pattern object.
For walkthrough on implementing this I'd recommend this guide which, covers almost any version of delphi, but if you're using Delphi 2010 you could also use the new class Constructors/Destructors for improved results.
You could create a class that contains nothing but static methods. If you have to maintain some sort of state, then the state variables should be passed as var parameters. There is no way to "properly" access static variables other than having a set of global variables in the implementation section of the class OUTSIDE the scope of the class, for example:
UNIT TestUnit;
INTERFACE
Type
TStaticClass = Class
public
procedure Foo(var Bar:String); static;
end;
IMPLEMENTATION
var
LastBar : String; // the last bar
LastRes : string; // the last result
Procedure TStaticClass.Foo(var Bar:String);
begin
if Bar <> LastBar then
LastRes := SomeCPUExpensiveProcess(Bar);
LastBar := Bar;
Bar := LastRes;
end;
INITIALIZATION
LastBar := '';
LastRes := SomeCPUExpensiveProcess('');
END.
You can also create a new unit called uDG_Utils for example, define a class, define a global variable for that class and in the initialization and finalization section you manage the class constructor and destructor.
Now all you need to do is call it like mySuperDuperClass.SuperMethod...
EDIT
I have edited this post to remove it. The answer is admittedly bad and deserves the down-votes. I don't want it to remain here to confuse or mislead anyone further.
If I write
type
MyClass = class of TMyClass;
...
Obj := MyClass.Create;
the correct constructor (the one in TMyClass) is called.
If I write
var
ClassVar : TClass;
...
ClassVar := TMyClass;
Obj := ClassVar.Create;
only the TObject constructor is called.
Why? What's the difference between the two versions? Can I force the TMyClass constructor call in the second scenario?
TClass is declared as "Class of TObject" in system.pas. What constructor gets called is decided at compile-time, and all the compiler knows is what base class you're using. It doesn't know what the value of the variable will be when it runs, so it has to default to the base class. If you're using a TClass, then your base class is TObject.
If you're using a class variable, I assume you have some sort of heirarchy and you're trying to implement a factory. If you want to make sure the right constructor gets called based on the value of a class variable at runtime, not something contained in your code at compile time, you need a virtual constructor.
type
TMyBaseObject = class(TObject)
public
constructor Create; virtual;
end;
TMyClass = class of TMyBaseObject;
Use a TMyClass instead of a TClass as your class variable, and now the compiler will generate a call to TMyBaseObject.Create, which is virtual. Make sure all your derived classes override the base constructor, and you'll end up calling the right one at runtime.
TObject.Create is not virtual, you need to declare ClassVar as a classtype of a class with a virtual constructor.
I would suggest you look into overriding the AfterConstruction method that's introduced by the TObject to make polymorphism like this work.
Each class definition can introduce a new constructor, with it's own set of parameters. A class variable only knows of the constructor if the base class it's a variable for. This is all possible because a constructor is neither virtual nor overridden. You could flag the constructor virtual on your base-class, and flag all descending classes override, which blocks the parameter list. (I think if you forget 'override' the compiler warns your new constructir hides the virtual one.)
Or descend it from TComponent, which already have a virtual constructor.