Interfaces implementieren: Object Pascal und C++

Aus Appmethod Topics
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 Object Pascal-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

Appmethod C++ XE führt das neue Attribut __property implements ein, mit dem in C++ Interfaces – analog zu der Verwendung der Delegation in Object Pascal – einfacher implementiert werden können. Weitere Informationen finden Sie unter Unterstützung von __property implements in Appmethod C++ XE.

Ab Appmethod C++ XE müssen Sie 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 Object Pascal und C++

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

Object Pascal-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 Object Pascal und C++.

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

Object Pascal

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(); }
 };

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

Das Object Pascal-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. Object Pascal verwendet in Eigenschaftsdeklarationen, die den unterstützenden Klassen zugeordnet werden, die Direktive implements. Diese Direktive bewirkt, dass der Object Pascal-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