Interfaces implementieren: Delphi und C++

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu C++-Spezifikation - Index


Dieses Thema beschreibt die beiden Methoden zum Implementieren von Interfaces in C++ im Vergleich zu der entsprechenden Delphi-Methode. Beachten Sie bitte, dass Ihnen in C++ zwei unterschiedliche Vorgehensweisen zum Implementieren von Interfaces zur Verfügung stehen:

  • Das neue Attribut __property implements
  • Vererbung

__property implements

Mit dem Attribut __property implements können Interfaces in C++ – analog zu der Verwendung der Delegation in Delphi – einfacher implementiert werden. Weitere Informationen finden Sie unter Unterstützung von __property implements in C++Builder.

Sie müsen die Methoden von IUnknown beim Implementieren von Interfaces in C++ nicht erneut implementieren und weiterleiten. Anstatt von TInterfacedObject abzuleiten, verwenden Sie TCppInterfacedObject: es behandelt IUnknown, und Sie behandeln die für Ihr Interface spezifische Methode.

Beispiel:

 class TMyPersist: public TCppInterfacedObject<IPersist>
 {
  HRESULT __stdcall GetClassID(CLSID *pClassID)
  {
    *pClassID = CLSID_SOMEVALUE;
    return S_OK;
  }
 };

Implementieren von Interfaces in Delphi und C++

Im Folgenden finden Sie zwei Implementierungsbeispiele: Eins aus Delphi und eins aus C++. Die beiden Beispiele stellen dasselbe Interface bereit, aber das Delphi-Beispiel wird im Gegensatz zum C++-Beispiel verzögert initialisiert.

Delphi-Beispiel

 unit Unit1;
 
 interface
 
 type
 
  // Interface, das eine 'Add'-Methode bereitstellt
  IAdder = interface
  ['{D0C74612-9E4D-459A-9304-FACE27E3577D}']
    function Add(I, J: Integer): Integer;
  end;
 
  // Aggregatee, der IAdder implementiert
  TAdder = class(TAggregatedObject, IAdder)
    function Add(I, J: Integer): Integer;
  end;
 
  // Aggregator - implementiert IAdder über TAdder
  TPasClass = class(TInterfacedObject, IAdder)
    FAdder: TAdder;
    function GetAdder: TAdder;
  public
    destructor Destroy; override;
    property Adder: TAdder read GetAdder write FAdder implements IAdder;
  end;
 
 function TestAdd(I, J: Integer): Integer; 
 
 implementation
 
 { TAdder }
 function TAdder.Add(I, J: Integer): Integer;
 begin
   Result := I+J;
 end;
 
 { TPasClass }
 destructor TPasClass.Destroy;
 begin
   FAdder.Free;
   inherited;
 end;
 
 function TPasClass.GetAdder: TAdder;
 begin
   if FAdder = nil then
     FAdder := TAdder.Create(Self as IInterface);
   Result := FAdder;
 end;
 
 // Hinzufügen mit TClass IAdder
 function TestAdd(I, J: Integer): Integer; 
 var
   punk: IInterface;
 begin
   punk := TPasClass.Create as IInterface;
   Result := (punk as IAdder).Add(I, J);
 end;
 
 end.

C++-Beispiel
Das C++-Beispiel verwendet keine verzögerte Initialisierung:

 #include <stdio.h>
 
 // Interface, das eine Add(..)-Methode bereitstellt
 __interface  INTERFACE_UUID("{D0C74612-9E4D-459A-9304-FACE27E3577D}") IAdder  : public    System::IInterface 
 {
    virtual int __fastcall Add(int I, int J) = 0 ;
 };
 typedef System::DelphiInterface<IAdder> _di_IAdder;
 
 
 // Aggregatee, der IAdder implementiert
 class TCppAdder: public TCppAggregatedObject<IAdder> {
 public:
    __fastcall TCppAdder(const _di_IInterface Controller) : _AGGREGATED_CLASS(Controller)
    {}
      
    int __fastcall Add(int i, int j) 
    {
        return i+j;
    }
 };
 
 
 // Aggregator - stellt IAdder über TCppAdder bereit
 class TCppClass : public TInterfacedObject {
 private:
    TCppAdder* obj; 
    IAdder* FAdder;
 protected:
    void initAggregatee() 
    {
        _di_IInterface punk;
        GetInterface(punk);
        _di_IAdder adder;
        (obj = new TCppAdder(punk))->GetInterface(adder);
        FAdder = adder;
    }
 public:
    __fastcall TCppClass()
    {
        initAggregatee();
    }
    __fastcall ~TCppClass()
    {
        delete obj;
    }
    __property IAdder* Adder = { read=FAdder, implements };
 };

 // Test: IAdder.Add(..) mit TCppClass aufrufen
 int main()
 {
    TCppClass* ptr; 
    _di_IAdder ia;
    if (!((ptr = new TCppClass())->GetInterface(ia)))
    {
        delete ptr;
    }
    else
    {
        int result = ia->Add(10, 20);
        printf("Add Result = %d\n", result);
    }
    return 0;
 }

Vererbung

Das folgende Beispiel vergleicht das Implementieren von Interfaces mithilfe der Vererbung in Delphi und C++.

Hier ist der Delphi-Code zum Erstellen einer Klasse, die das Interface IPersist zum Definieren einer einzelnen Methode, GetClassID, implementiert:

Delphi

Interface

 IPersist = interface(IUnknown)
    ['0000010C-0000-0000-C000-000000000046']
    function GetClassID(out classID: TCLSID): HResult; stdcall;
 end;

Klasse

 TMyPersist = class(TInterfacedObject, IPersist)
    function GetClassID(out classID: TCLSID): HResult; stdcall;
 end;
 
 function TMyPersist.GetClassID(out classID: TCLSID): HResult;
 begin
   classID := CLSID_SOMEVALUE;
   Result := S_OK;
 end;

Zum Vergleich hier der C++-Code zum Erstellen derselbe Klasse bei der Verwendung der Vererbung:

C++

Interface

 struct __declspec(uuid("0000010c-0000-0000-C000-000000000046")) 
 IPersist : public IUnknown
 {
  public:
     virtual HRESULT __stdcall GetClassID(CLSID *pClassID) = 0;        
 };

Klasse

 class TMyPersist: public TInterfacedObject, IPersist
 {
  HRESULT __stdcall GetClassID(CLSID *pClassID)
  {
    *pClassID = CLSID_SOMEVALUE;
    return S_OK;
  }
 
  // Methoden von of IUnknown müssen erneut implementiert und weitergeleitet werden :(
  HRESULT __stdcall QueryInterface(const GUID& IID, void **Obj)
 		            { return GetInterface(IID, Obj) ?
 		                      S_OK : E_NOINTERFACE; }
  ULONG __stdcall AddRef()  { return TInterfacedObject::_AddRef();  }
  ULONG __stdcall Release() { return TInterfacedObject::_Release(); }
 };

ActiveX unterstützende Klassen

Ein typisches ActiveX-Steuerelement implementiert mindestens 21 Interfaces, daher ist die Implementierung von Interfaces in C++Builder ohne Verwendung des Attributs __property implements ziemlich aufwendig.

Das Delphi-ActiveX-Framework (DAX) stellt unterstützende Klassen (wie TConnectionPoints und TPropertyPageImpl) bereit, deren übergeordnete Klassen (in diesem Fall TActiveXControl und TActiveXPropertyPage) nicht direkt von den Interfaces abgeleitet sind, die von unterstützenden Klassen implementiert wurden. Delphi verwendet in Eigenschaftsdeklarationen, die den unterstützenden Klassen zugeordnet werden, die Direktive implements. Diese Direktive bewirkt, dass der Delphi-Compiler das Interface in die RTTI InterfaceTable-Tabelle der Klasse aufnimmt.

Entsprechend bewirkt das Attribut __property implements, dass der C++-Compiler das Interface in die RTTI InterfaceTable-Tabelle der Klasse aufnimmt. Diese Tabelle enthält, wie IUnknown.QueryInterface implementiert ist, und teilt so anderen Objekten mit, dass eine bestimmte Klasse ein Interface implementiert.

Im obigen C++-TMyPersist-Beispiel ruft QueryInterface einfach TObject.GetInterface auf:

 // Methoden von of IUnknown müssen erneut implementiert und weitergeleitet werden :(
  HRESULT __stdcall QueryInterface(const GUID& IID, void **Obj)
 			    { return GetInterface(IID, Obj) ?
 			                      S_OK : E_NOINTERFACE; }

TObject.GetInterface wird aus der InterfaceTable in den Metadaten einer Klasse entfernt. Wenn es keinen InterfaceTable-Eintrag für ein Interface gibt, muss die Klasse für die Behandlung aller Interfaces, die sie implementiert, eine Roh-Implementierung von QueryInterface enthalten, anstatt dass die in TObject vorhandene Logik diese Aufgabe erledigt. Deshalb verwenden COM-Entwickler Frameworks, wie ATL, MFC, BaseCtl und DAX, anstatt für IUnknown Roh-Implementierungen zu schreiben.

C++ könnte die Vorteile der von DAX bereitgestellten, unterstützenden ActiveX-Klassen ohne die Unterstützung von __property implements nicht nutzen.

Siehe auch