4

Are there any side effects to changing a class hierarchy's ancestor from TObject to TInterfacedObject so that I can implement interfaces further down the inheritance chain?

I've programmed in Delphi for several years but never encountered interfaces. I became accustomed to using them in other languages. Now that I'm involved in a Delphi project again I'd like to start taking advantage of them but I know they work a bit differently than in Java or C#.

Kenneth Cochran
  • 11,954
  • 3
  • 52
  • 117

3 Answers3

4

If you already have existing code using the class you will probably have to modify a lot of it to keep references to interfaces instead of object instances. Interfaces are reference counted and released automatically, as a result, any reference to the implementor instance will become an invalid pointer.

Ondrej Kelle
  • 36,941
  • 2
  • 65
  • 128
  • In most cases the objects are created and destroyed in a single function. My main concern was whether reference counting interfered with manual memory management. Its a huge code base with very few unit tests so I planned to replace the references incrementally. – Kenneth Cochran Oct 28 '10 at 16:43
  • 2
    @codeelegance: Yes, interface counting replaces manual memory management. You can use one or the other, but not both, on any given object. – Mason Wheeler Oct 28 '10 at 16:48
  • @Mason So as long as I don't have an object reference and an interface reference pointing to the same object I should be alright? – Kenneth Cochran Oct 28 '10 at 16:54
3

This will work fine as long as you inherit from the class below at the top (bottom?) of your hierarchy. This code ensures that your new classes dont free themselves - as is the default behaviour of TInterfaceObject - you are presumably already freeing them yourself and want to preserve this. This activity is actually exactly what TComponent in the VCL does - it supports interfaces but is not reference counted.

type


  TYourAncestor = class( TInterfacedObject )
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;

  end;



implementation



function TYourAncestor.QueryInterface(const IID: TGUID; out Obj): HResult;
const
  E_NOINTERFACE = HResult($80004002);
begin
  if GetInterface(IID, Obj) then Result := 0 else Result := E_NOINTERFACE;
end;


function TYourAncestor._AddRef: Integer;
begin
  Result := -1   // -1 indicates no reference counting is taking place
end;

function TYourAncestor._Release: Integer;
begin
  Result := -1   // -1 indicates no reference counting is taking place
end;
Brian Frost
  • 13,334
  • 11
  • 80
  • 154
  • Let's say I didn't reimplement these three functions and attempted to manually free an object from an interface reference. What would be the result? – Kenneth Cochran Oct 28 '10 at 16:53
  • @codeelegance: the result would be a compiler error, it is not possible to free an object from an interface reference. – mjn Oct 28 '10 at 17:49
  • You don't need to inherit from TInterfacedObject here, TObject implementing IUnknown would be fine. – tomazy Oct 28 '10 at 19:42
2

Aside from a few extra bytes in your instance size, no. That's probably the best way to do it.

Mason Wheeler
  • 82,511
  • 50
  • 270
  • 477