2008-11-14 8 views
12

In C++, ist es möglich, eine Basis und abgeleitete Klasse implementieren eine einzige Schnittstelle?C++: Derived + Base-Klasse implementieren eine einzige Schnittstelle?

Zum Beispiel:

class Interface 
{ 
    public: 
     virtual void BaseFunction() = 0; 
     virtual void DerivedFunction() = 0; 
}; 

class Base 
{ 
    public: 
     virtual void BaseFunction(){} 
}; 

class Derived : public Base, public Interface 
{ 
    public: 
     void DerivedFunction(){} 
}; 

void main() 
{ 
    Derived derived; 
} 

Dies schlägt fehl, da Abgeleitet nicht instanziiert werden kann. Soweit der Compiler betroffen ist, ist Interface :: BaseFunction niemals definiert.

Bisher ist die einzige Lösung, die ich gefunden habe wäre ein Pass-Through-Funktion in Abgeleitet

class Derived : public Base, public Interface 
{ 
    public: 
     void DerivedFunction(){} 
     void BaseFunction(){ Base::BaseFunction(); } 
}; 

Gibt es eine bessere Lösung zu erklären?


EDIT: Wenn es darauf ankommt, ist hier eine reale Welt Problem, das ich MFC Dialoge hatte verwenden.

Ich habe eine Dialogklasse (MyDialog lässt sagen), die von CDialog abgeleitet ist. Aufgrund von Abhängigkeitsproblemen muss ich eine abstrakte Schnittstelle (MyDialogInterface) erstellen. Die Klasse, die MyDialogInterface verwendet, muss die für MyDialog spezifischen Methoden verwenden, muss aber auch CDialog :: SetParent aufrufen. Ich habe es gerade gelöst, indem ich MyDialog :: SetParent erstellt habe und es zu CDialog :: SetParent weitergeleitet habe, aber ich habe mich gefragt, ob es einen besseren Weg gibt.

Antwort

16

C++ bemerkt nicht, dass die von Base geerbte Funktion bereits implementiert BaseFunction: Die Funktion muss explizit in einer von Interface abgeleiteten Klasse implementiert werden. Ändern Sie es so:

class Interface 
{ 
    public: 
     virtual void BaseFunction() = 0; 
     virtual void DerivedFunction() = 0; 
}; 

class Base : public Interface 
{ 
    public: 
     virtual void BaseFunction(){} 
}; 

class Derived : public Base 
{ 
    public: 
     virtual void DerivedFunction(){} 
}; 

int main() 
{ 
    Derived derived; 
} 

Wenn Sie wollen in der Lage zu entkommen mit nur einer von ihnen Implementierung aufgeteilt Interface in zwei Schnittstellen:

class DerivedInterface 
{ 
    public: 
     virtual void DerivedFunction() = 0; 
}; 

class BaseInterface 
{ 
    public: 
     virtual void BaseFunction() = 0; 
}; 

class Base : public BaseInterface 
{ 
    public: 
     virtual void BaseFunction(){} 
}; 

class Derived : public DerivedInterface 
{ 
    public: 
     virtual void DerivedFunction(){} 
}; 

class Both : public DerivedInterface, public Base { 
    public: 
     virtual void DerivedFunction(){} 
}; 

int main() 
{ 
    Derived derived; 
    Base base; 
    Both both; 
} 

Hinweis: Haupt zurückgeben muss int
Hinweis: Es empfiehlt sich, virtual vor Elementfunktionen in den abgeleiteten Dateien zu halten, die in der Basis virtuell waren, auch wenn dies nicht unbedingt erforderlich ist.

+0

Wenn Sie es so machen, dann brauchen Sie nicht die zweite Erklärung von BaseFunction. – Torlack

+0

Es ist was er geschrieben hat. Ich wollte es nicht ändern. Er könnte einen Grund haben es so zu ändern –

+0

Ich habe einen Kommentar dazu gemacht. Danke :) –

1

Das Problem ist, dass mit Ihrem Beispiel, haben Sie zwei Implementierungen von , die aus Base und die aus Derived kommen. Dies ist per Entwurf in der Sprache C++. Wie bereits erwähnt, entfernen Sie einfach die Basisklasse Interface in der Definition Derived.

+0

um .. nein .... Prüfe den Code nochmal. Base erbt nicht von Interface. (Schnittstelle hat die DerviceFunction, die nicht in Base implementiert ist) –

4

Es sieht so aus, als ob die abgeleitete "is-a" -Basis nicht ganz der Fall ist, was darauf hindeutet, dass Containment eine bessere Implementierung als Vererbung sein kann.

Auch Ihre Derived-Member-Funktionen sollten ebenso virtuell deklariert werden.

Sie können das enthaltene Element zu einem Verweis oder einem intelligenten Zeiger machen, wenn Sie die Implementierungsdetails ausblenden möchten.

1

Ich stimme der Antwort von Litb. Hier besteht jedoch die Möglichkeit, etwas darüber zu verstehen, wie virtuelle Funktionen und Mehrfachvererbung funktionieren.

Wenn eine Klasse mehrere Basisklassen hat, verfügt sie über separate vtables für jede Basisklasse.Derived eine VTable Struktur haben, die wie folgt aussieht:

Derived 
vtable: Interface 
    BaseFunction* 
    DerivedFunction* 
vtable: Base 
    BaseFunction* 

Zusätzlich kann jede Basisklasse wird nur in der Lage zu sehen, seine eigene VTable. Wenn Base instanziiert wird, füllt es den Zeiger Base::BaseFunction in der vtable aus, kann aber die vtable für Interface nicht sehen.

Wenn der Code, den Sie die resultierende VTable Struktur einer Instanz Derived bereitgestellt könnte kompilieren, würde wie folgt aussehen:

Derived 
vtable: Interface 
    BaseFunction* = 0 
    DerivedFunction* = Derived::DerivedFunction 
vtable: Base 
    BaseFunction* = Base::BaseFunction 
0

fand ich eine Sache fehlt aus litb Antwort. Wenn ich eine Derived Instanz habe, kann ich eine DerivedInterface und BaseInterface bekommen. Aber wenn ich nur eine DerivedInterface habe, kann ich nicht BaseInterface bekommen, da das Ableiten DerivedInterface von BaseInterface nicht funktioniert.

Aber diese ganze Zeit beschränkte ich mich auf die Kompilierzeitprüfung aus irgendeinem Grund. Diese DerivedInterface funktioniert einfach toll:

class DerivedInterface 
{ 
    public: 
     virtual void DerivedFunction() = 0; 
     BaseInterface* GetBaseInterface() 
      {return dynamic_cast<BaseInterface*>(this);} 
}; 

void main() 
{ 
    Derived derived; 

    DerivedInterface* derivedInterface = &derived; 
    derivedInterface->GetBaseInterface()->BaseFunction(); 
} 

Kein Durchlauf durch Funktionen, die notwendig in Derived, und jeder ist glücklich. Sicher, es ist nicht mehr streng eine Schnittstelle, aber das ist in Ordnung. Warum habe ich nicht früher daran gedacht? :)

+0

das ist kein Problem. Sie machen nur eine Klasse von DerivedInterface und BaseInterface ableiten, und dann haben Sie genau die Schnittstelle Ihrer "Interface" -Klasse in Ihrer ursprünglichen Frage –

+1

sowieso. Verwenden Sie dynamic_cast niemals so :) dynamic_cast sollte ein letzter Ausweg sein, um das zu umgehen, was nicht möglich ist, indem Sie korrekte Schnittstellen entwerfen. In deinem Fall, mach einfach Interface, wie ich oben im Kommentar gesagt habe, und dann kannst du BaseInterface * baseInterface = & abgeleitete; und rufe beide :) –

+0

Ja, das wird mir die selbe Schnittstelle geben, aber eine andere Implementierung, das ist der springende Punkt. Ich habe die Implementierung für eine Basis und abgeleitete Klasse, und ich brauche eine einzige Schnittstelle für sie.Es hört sich so an, als wäre ich so nah dran. – Joe

Verwandte Themen