2013-12-22 3 views
5

gelten folgende Voraussetzungen:Kann ich verschiedene Implementierungen derselben Funktion aus verschiedenen Basisklassen implementieren?

class A{ virtual void f() = 0; }; 
class B{ virtual void f() = 0; }; 

Kann ich irgendwie das folgende?

class C : public A, public B 
{ 
    virtual void A::f(){ printf("f() from A"); } 
    virtual void B::f(){ printf("f() from B"); } 
}; 

So jetzt kann ich ???

A* pa = new C(); 
pa->f(); // prints f() from A; 
B* pb = (B*)pa; 
pb->f(); // prints f() from B; 

Vielen Dank !!!

+1

Seien Sie vorsichtig mit Mehrfachvererbung. Was versuchst du zu machen? – Manu343726

+0

@ Manu343726 nicht auf eine Tangente gehen, aber mehrfache Vererbung mit ABCs wird im Allgemeinen als in Ordnung betrachtet. –

+0

@Tim ich stimme zu, es ist in Ordnung, ich schlage ihm nur vor, vorsichtig zu sein. Was ich nicht verstehe, ist, was er mit diesem Design zu erreichen versucht. – Manu343726

Antwort

5

Erste Lösung

Diese Frage erinnern die 'Fassade' Design-Muster. Dies sollte als diese neu zu schreiben werden:

class AC : public A 
{ public: virtual void f(){ cout << "f() from A" << endl;}; }; 

class BC : public B ... 

class C : public AC, public BC {}; 

wobei C die ‚Fassade‘ ist.

C* c = new C(); 
c->AC::f(); 
c->BC::f(); 

Wenn Sie zwischen AC & BC sollte dies die Arbeit tun, wie es offuscated is'nt jede Aktie Einschränkung nicht haben:

So in der richtigen Aufrufsyntax so etwas wie das sein sollte.

Zweite Lösung

Ein andere Lösung, Dank an Casey (siehe ersten Kommentar), ist eine Vorwärts-Deklaration der Klasse C in einer Vorlage verwenden, um Anrufe zu Methoden letztere definieren zu können.

template <typename C> 
class AC : public A { 
public: 
    void f() { static_cast<C&>(*this).f_from_A(); } 
}; 

template <typename C> 
class BC : public B { ... }; 

so kann der Implementierungsteil in der gleichen Klasse erfolgen.

Der anrufende Teil ist sauberer, weil es zeigt keine Implementierungsdetails und es am nächsten an der Frage ist:

C* c = new C(); 
((A*) c) -> f(); 
((B*) c) -> f(); 

Es ist nicht mehr ‚default‘ f() auf C und es ist möglich, das erwartete Verhalten der Vererbung zu brechen, und es ist schwerer zu lesen.

+1

Wenn Sie die Aufrufe in einer CRTP-Klasse neu zuordnen, können Ihre Implementierungen tatsächlich in "C" mit vollem Zugriff auf das Objekt definiert werden ([Beispiel bei Coliru]) (http://coliru.stacked-crooked.com/a/1097abe9a3292dbf)). – Casey

0

Als Referenz scheinen die folgenden Links bezogen werden:

  1. Overloading virtual functions of the same name from different base classes. Is it possible?
  2. C++ virtual override functions with same name
  3. Derived class defines function via base class

Die OP in der zweiten Verbindung gefragt, ob er etwas tun könnte, wie Sie gefragt:

class Impl : public A , public B 
{ 
    public: 
    void A::Function() { cout << "A::Function" << endl; } 
    void B::Function() { cout << "B::Function" << endl; } 
}; 

Johannes Antwort lautet:

class RemapA : public A 
{ 
    virtual void fnInA() = 0; 
public: 
    virtual void fn() 
    { 
     fnInA(); 
    } 
}; 

class RemapB : public B 
{ 
    virtual int fnInB() = 0; 
public: 
    virtual int fn() 
    { 
     return fnInB(); 
    } 
}; 

class C : public RemapA, public RemapB 
{ 
    virtual void fnInA() { /* ... */ } 
    virtual void fnInB() { /* ... */ } 
    // ... 
}; 

Herb Sutter Link sagt im Wesentlichen das Gleiche:

You cannot use qualified names there. I you write void Function() { ... } you are overriding both functions. Herb Sutter shows how it can be solved.

Another option is to rename those functions, because apparently they do something different (otherwise i don't see the problem of overriding both with identical behavior).

So eine mögliche Lösung (von James Kanze aus dem ersten Link) sieht wie folgt aus.

0

Ja. Jede Basisklasse hat ihre eigene virtuelle Tabelle.
So wird der Compiler durch Casting die virtuelle Tabelle des Typs, der Sie werfen, überprüfen und den Zeiger auf die entsprechende Funktion dort erhalten.

GCC unterstützt dies nicht. Nur Visual Studio funktioniert.

Aber Sie müssen immer eine Besetzung oder eine Bereichsfunktion aufrufen.
Dies wird wieder eine zweideutige Anruffehler:

C* c = new C(); 
c->f(); //ambiguous call compilation error 

This will work: 
((B*)c)->f(); 

Der Compiler zeigt die Struktur wie folgt aus: (Visual Studio-Flag: /d1reportSingleClassLayout C)

class C size(8): 
    +--- 
    | +--- (base class A) 
    0 | | {vfptr} 
    | +--- 
    | +--- (base class B) 
    4 | | {vfptr} 
    | +--- 
    +--- 

    C::[email protected]@: 
    | &C_meta 
    | 0 
    0 | &C::f 

    C::[email protected]@: 
    | -4 
    0 | &CCCC::f 

    C::f this adjustor: 4 
    C::f this adjustor: 0 

Grundsätzlich können Sie die 2 separate Zeiger sehen für die passenden virtuellen Tabellen.
So könnten Sie tun:

C* c= new C(); 
    B* b = (B*)c; 
    b->f(); // calls C::B::f() 
    A* a = (A*)c; 
    a->f(); // calls C::A::f() 

    or even: 

    a = (A*)(C*)b; 
    a->f(); // calls C::A::f() 

Beachten Sie, dass Sie benötigen b zurück in die abgeleitete Klasse Fall die virual Tabelle der A Typ Basisklasse zuzugreifen.

+0

Dies beantwortet nicht, wie 'C' die zwei Funktionen überschreiben sollte. Sie sind in den Basisklassen abstrakt. – hvd

+0

@hvd Ich habe diesen Teil hinzugefügt. –

+0

@hvd und das "wie" ist genauso wie er es tat. –

0

Zum Überschreiben beider Funktionen benötigen Sie Zwischenklassen. Eine Verwendungsdeklaration in C wählt die Funktion von A:

#include <iostream> 

struct A{ virtual void f() = 0; }; 
struct B{ virtual void f() = 0; }; 

struct IntermediateA : public A { 
    virtual void f() override { std::cout << "A\n"; } 
}; 

struct IntermediateB : public B { 
    virtual void f() override { std::cout << "B\n"; } 
}; 


struct C : public IntermediateA, public IntermediateB 
{ 
    using IntermediateA::f; 
}; 

int main() { 
    C c; 
    B* b = &c; 

    c.f(); 
    b->f(); 
    return 0; 
} 
Verwandte Themen