Mehrfachvererbung im Package
-
Hallo,
ich hoffe ihr könnt mir weiterhelfen.
wenn ich folgendes versuche zu Kompilieren, kommt die Warnung:
W8130 Interface 'Basis' ist nicht von IUnknown abgeleitet. (Interfaces müssen von IUnknown abgeleitet sein)
class Basis { public: virtual void __fastcall funktion1()=0; } class PACKAGE tMain : public TComponent,Basis { public: void __fastcall funktion1(){}; }
Kann ich die Warnung Ignorieren oder ist da etwas grob fahrlässig dran?
-
semikolon hinten dran?
ich weiß nicht, ob es daran liegt, aber normalerweise sollte bei fehlendem semikolon hinter klassen eigentlich sofort ein Compilerfehler auftauchener fasst deine Klasse warum auch immer als Interface auf
-
Semikolon ist nicht das Problem. Kompilieren kann ich es ja. Also Syntaxfehler sind nicht drin. Es kommt ja nur eine Warnung. Will nur wissen warum diese Warnung kommt obwohl in meinen Büchern drin steht das es so richtig ist.
-
dann kann ich dir nicht helfen, tut mir leid
ich habe allerdings noch nie gesehen, dass man Klassen ohne abschließendes Semikolon programmieren darf, bei mir gab das immer Syntaxfehler.
Wir reden hier aber über reines C++ ohne CLI, auf einem Windows PC?
Ansonsten:
Warnungen habe ich (fast) noch nie beachtet.
Ist vielleicht nicht unbedingt die beste Lösung, aber da es ja kein Fehler ist, funktioniert es ja anscheinend.
Ich glaube, dass die Warnung zustande kommt, weil der Compiler jede Klasse, die nur Funktionen, keine Variablen hat, sofort als interface auffasst (letztlich ist es da ja auch). Und die haben halt gerne, dass man interfaces ans COM anhängt, also von IUnknown ableitet. Eigentlich brauchst du IUnknown nicht, wenn du jetzt nicht riesige Sachen programmieren willst.
-
Die VCL unterstützt Mehrfachverbung nur beschränkt, es gibt Anforderungen an die Basisklasse. Sie ist eigentlich nur ein Interface mit Deklarationen, darf also nur virtuelle Methoden enthalten. Außerdem muss ihr eine uuid zugeteilt werden, damit sie hinterher in der Hierarchie wiedergefunden werden kann.
Zu guter Letzt brauchst du noch eine Funktion, die dir den Zeiger auf dein Interface zurückliefert, normale C/C++ casts funktionieren hier nicht.struct __declspec( uuid( "{EB1A405F-D278-4627-9A94-7A9CEA228BC8}" ) ) MyIntf { virtual void some_func() = 0; }; class MyClass : public TComponent, public MyIntf { .. }; template<typename DestT> DestT vcl_dynamic_cast( TObject* obj ) { if( obj ) { TInterfaceEntry* Entry = obj->GetInterfaceEntry ( obj->ClassType(), __uuidof( DestT ) ); if( Entry ) return (DestT) ((int) obj + Entry->IOffset); } return 0; }
-
Vorab: Mehrfachvererbung ist was anderes als das, was du machen willst. Du willst Interfaces implementieren.
Frageman123 schrieb:
W8130 Interface 'Basis' ist nicht von IUnknown abgeleitet. (Interfaces müssen von IUnknown abgeleitet sein)
Daß der Compiler diese Warnung wirft, habe ich mitzuverantworten. Wenn es nur nach mir gegangen wäre, wäre es eine Fehlermeldung geworden.
Die Warnung ist ernstzunehmen, weil COM-Interfaces und abstrakte C++-Basisklassen nicht kompatibel sind. Die Klasse
Basis
ist, da weder von IUnknown noch von TObject abgeleitet, eine handelsübliche abstrakte C++-Klasse, wohingegen tMain eine Delphi-Klasse ist, die einem völlig anderen Klassenmodell folgt. Daß der Compiler jemals diese Art von Vererbung zugelassen hat, war ein Designfehler mit fatalen Folgen.Leider ist das Problem wenig verstanden, weshalb du jetzt auch Ratschläge bekommst, das sei nicht so wild und man könne das ganz locker sehen. Gerade DocShoe müßte es besser wissen, weil er Interface-Vererbung vor fünf Jahren ebenso unbedacht verwendet hat wie du und schon damals über die unvermeidlichen Konsequenzen geklagt hat. (Der Thread ist übrigens sehr aufschlußreich, wenn du mehr über die Hintergründe wissen willst.)
Um es also kurz zusammenzufassen: ja, das ist grob fahrlässig und sollte vom Compiler eigentlich mit einer Fehlermeldung quittiert werden. Verschiedene Konstrukte in Compiler und RTL verlassen sich darauf, daß ein Objekt entweder eine Delphi-Klasse oder eine C++-Klasse ist; dein
tMain
ist aber beides ein bißchen und keines richtig. Im besten Fall hast du nur Compilerwarnungen oder kannst nicht casten, wie du es dir vorstellst; im schlimmsten Fall bekommst du zur Laufzeit Zugriffsverletzungen an den unmöglichsten Stellen, weil du den Compiler über die "Art" des Objektes (Delphi- oder C++-Klasse) getäuscht hast.Hier habe ich ein bißchen was zu dieser Warnung geschrieben, und auch, wie man es stattdessen richtig macht. Nimm dir die Zeit und lies es durch.
Ein kurzes Beispiel, wie man es richtig macht:
class IBasis : public IUnknown // eine UUID kannst du angeben, brauchst du aber nicht { public: virtual void some_func (void) = 0; }; typedef DelphiInterface<IBasis> _di_IBasis; class PACKAGE TMain : public TComponent, public IBasis { public: INTFOBJECT_IMPL_IUNKNOWN(TComponent) // implementiert die Methoden von IUnknown void some_func (void); // implementiert dein Interface };
DocShoe schrieb:
Zu guter Letzt brauchst du noch eine Funktion, die dir den Zeiger auf dein Interface zurückliefert, normale C/C++ casts funktionieren hier nicht.
Die Funktion heißt
interface_cast<>()
, und sie ist bereits in der RTL enthalten, so daß du das Rad nicht neu erfinden mußt.interface_cast<>()
funktioniert außerdem in beide Richtungen, d.h. von Interface zu Objekt und Objekt zu Interface (letzteres aber nur, wenn dein Interface eine UUID hat). Beispiele zur Verwendung findet man unter obigem Link.
-
Hallo, ich saß vor der gleichen Aufgabenstellung und verwende jetzt
INTFOBJECT_IMPL_IUNKNOWN(TComponent)
zur Implementierung von IUnknown. Leider bekomme ich dabei folgende Warnung:
[bcc32 Warnung] FrmCANopen.h(174): W8022 '__stdcall TFormCANopen::QueryInterface(const _GUID &,void * *)' verbirgt virtuelle Funktion '__stdcall TCustomForm::QueryInterface(const _GUID &,void *)'
Ich konnte dazu leider nichts finden und kann auch nicht beurteilen, ob ich die Warnung ignorieren kann ...
Hat jemand eine Idee?
VG
-
Die beiden Methoden haben unterschiedliche Parameter und die Methode der abgeleiteten Klasse überdeckt die Methode der Basisklasse.
Bist du dir sicher, dass die Methode in der abgeleiteten Klasse die richtigen Parametertypen benutzt? Falls das so ist kannst du mit folgender Zeile in der KlasseTFormCANopen
das Problem lösen:class TFormCANopen ... { ... using TCustomForm::QueryInterface; ... };
-
Kerem schrieb:
Leider bekomme ich dabei folgende Warnung:
[bcc32 Warnung] FrmCANopen.h(174): W8022 '__stdcall TFormCANopen::QueryInterface(const _GUID &,void * *)' verbirgt virtuelle Funktion '__stdcall TCustomForm::QueryInterface(const _GUID &,void *)'
Ich konnte dazu leider nichts finden und kann auch nicht beurteilen, ob ich die Warnung ignorieren kann ...
Ja, die Warnung kannst du ignorieren.
Die Ursache ist, daß die Typsignatur von
TInterfacedObject::QueryInterface()
undIUnknown::QueryInterface()
unterschiedlich ist, was wiederum daran liegt, daßQueryInterface()
in Delphi anders repräsentiert ist. C++ hat ja leider keinoverride
-Keyword, mit dem man anzeigen könnte, daß man eine Methode aus einer Basisklasse überschreiben möchte. Wenn es also eine virtuelle Methode gleichen Namens in der Basisklasse gibt, die aber eine andere Funktionssignatur hat, nimmt der Compiler vorsichtshalber an, daß du wahrscheinlich die virtuelle Methode aus der Basisklasse überschreiben wolltest, und weist dich darauf hin, daß das nicht passiert und die Methode stattdessen verborgen wird, weil eben die Funktionssignatur nicht übereinstimmt. Allerdings wollen wir ja gerade nicht die Methode überschreiben, sondern eine zufällig gleichnamige Interface-Methode implementieren. Und das kann der Compiler nicht wissen ohne einoverride
- oderreintroduce
-Keyword.Du kannst die Warnung umgehen, indem du eine Überschreibung der Methode aus TComponent hinzufügst (das
using
-Statement von DocShoe sollte es eigentlich auch tun, tut es aber in meinen Versuchen nicht):class PACKAGE TMain : public TComponent, public IBasis { public: INTFOBJECT_IMPL_IUNKNOWN(TComponent) // implementiert die Methoden von IUnknown HRESULT STDMETHODCALLTYPE QueryInterface (const GUID& iid, void* ptr) { return TComponent::QueryInterface (iid, ptr); } void some_func (void); // implementiert dein Interface };
Idealerweise hätte das bereits Teil des
INTFOBJECT_IMPL_IUNKNOWN()
-Makros sein müssen.
-
Vielen Dank für die schnelle Hilfe!
Leider hat auch bei mir DocShoes Methode keine Wirkung gezeigt.