Any interface in Delphi inherits from IInterface (which is a nickname for IUnknown). It is nice for reference-counted interfaces, but sometimes we do not need reference counting at all. Consider the following example:
unit IValues; interface type IValue = interface(IInterface) function GetValue: Integer; end; function GetIValue: IValue; implementation type TValueObject = class(TObject, IValue) protected FValue: Integer; function GetValue: Integer; public constructor Create(AValue: Integer); end; var FValueObject: TValueObject; function GetIValue: IValue; begin Result:= FValueObject; end; constructor TValueObject.Create(AValue: Integer); begin FValue:= AValue; end; function TValueObject.GetValue: Integer; begin Result:= FValue; end; initialization FValueObject:= TValueObject.Create(11); finalization FValueObject.Free; end.
The above code does not compile because TValueObject does not implement IUnknown. A workaround is to implement stub methods for IUnknown:
type TValueObject = class(TObject, IValue) protected FValue: Integer; function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; function GetValue: Integer; public constructor Create(AValue: Integer); end; function TValueObject.QueryInterface(const IID: TGUID; out Obj): HResult; begin if GetInterface(IID, Obj) then Result:= S_OK else Result:= E_NOINTERFACE; end; function TValueObject._AddRef: Integer; begin Result:= -1; end; function TValueObject._Release: Integer; begin Result:= -1; end;
You can find the same code in VCL sources (TCustomVariantType class from Variants.pas unit).
Now, why should I implement IUnknown in my code if I don’t need it at all?
The above implementation of IUnknown looks like a standard implementation for non-reference-counted interfaces in Delphi. Why not to include it into System.pas and apply compiler magic to make the code in the first example compilable and legitimate? The implementation does not use additional object fields (TInterfacedObject implementation requires additional FRefCount field), so it seems like all that is required from the compiler is to insert pointers to the default IUnknown method’s implementations into the interface tables of any class that does not implement or inherit these methods can be implemented directly in TObject. It will save the programmer from writing a useless duplicate code. The compiler may also issue a warning for the safety reasons, if necessary.
Any implemented interface requires a hidden 4 byte field in the data of the object. An interface pointer is a pointer to a pointer to an array of pointers for the methods. That “pointer to an array of pointers for the methods” is what is being stored in the hidden field.
So adding IUnknown to TObject would increase the size of all objects on the heap by 4 bytes.
That is true if only IUnknown is actually implemented in TObject, but I did not mean it. I meant any class that implements some interface but does not implement IUnknown methods may inherit the IUnknown part of the interface from TObject
TValueObject can subclass TSingletonImplementation from Generics.Defaults instead of TObject to get the implementation for the missing methods.
/Micke
You can also use the TSingletonImplementation from the Generics.Defaults unit as a base class:
–jeroen
Duh – too late.
I can’t believe that nobody has yet mentioned that the biggest danger here is the intention to mix reference counted objects with objects with an explicit managed lifetime.
That is fine as far as it goes, but the compiler will inject AddRef() and Release() calls around code that obtains and relinquishes interface references, whether or not the implementing object wants to be reference counted.
If you Free and object that has outstanding interface references to it you will get AV’s when those references go out of scope, even tho you have no code making explicit calls to the object involved!!
You will sweat blood trying to debug that sort of mess!!!
This is why it is essentially “safe” on a singleton (which is presumably intended to be explicitly destroyed when the process terminates, when all other references have – or should have – dropped out of scope), but is why applying the technique more generally is A Bad Idea™ unless you know what you are doing.
And if you know what you are doing you can (and probably already have) implement IUnknown yourself.
🙂
There is no intentention to mix reference counted objects with explicitly managed objects, so I don’t see a danger here.
A real danger is to do without knowing what you are doing. 🙂
If you keep reference to object then you mix interface with object even if you TSingleton Implementation of IUnknown.
With your source try this:
See this:
http://delphi.uservoice.com/forums/4433-language-features/suggestions/179044-pure-interface?ref=title
@Macedonczyk
TValueObject is defined below implementation so you can’t do that.
As long as the developer that changes in IValues.pas know what he is doing you as a user of IValues is protected.
@jpluimers & Mikael Eriksson : thanks for TSingletonImplementation from Generics.Defaults remark, still I’d prefer that the basic functionality was implemented in TObject (just methods to be inherited by descendant classes implementing interfaces, TObject itself should not excibit IUnknown)
@Macedonczyk
Hi.
Interesting topic about problems mixing objects with references.
I’m often use the following approach and I never get AV. I’m not sure if this is wrong or not, but works well for me. I also not sure what happens internally. Using Delphi 2007
First, subclass TValueObject from TInterfacedObject instead from TObject. TInterfacedObject implements the methods of IInterface, so I don’t need implement those by myself.
I been just lucky for not get AV?
The TValueObject should just inherit from TInterfacedObject instead of TObject.
TValueObject = class(TInterfacedObject, IValue)
16 protected
17 FValue: Integer;
18 function GetValue: Integer;
19 public
20 constructor Create(AValue: Integer);
21 end;
then TInterfacedObject implements the 3 functions
Please see:
http://delphi.about.com/od/oopindelphi/a/interfaces-in-delphi-programming-101.htm
Seems you don’t paid much attention to what was said.
TInterfacedObject implements reference counting on interfaces references, what is we want to avoid here… We look for options to use interfaces without reference counting and without having to create stubs for IUnknown methods.