2016-04-04 15 views
1

nicht sicher, wie es auch zu erklären, so dass ich nur eine Stichprobe von Code, der mein Problem zeigt:Aufruf angegebene Methode aus der Basisklasse

class Base { 
public: 
    Base() = default; 
    ~Base() = default; 

    virtual void stuff(std::shared_ptr<Base> b) = 0; 
}; 

class DerivedA : public Base { 
public: 
    DerivedA() = default; 
    ~DerivedA() = default; 

    void stuff(std::shared_ptr<Base> b) { 
    std::cout << "stuff Base" 
       << "\n"; 
    } 
}; 

class DerivedB : public Base { 
public: 
    DerivedB() = default; 
    ~DerivedB() = default; 

    void stuff(std::shared_ptr<Base> b) { 
    std::cout << "stuff Base" 
       << "\n"; 
    } 
    void stuff(std::shared_ptr<DerivedA> b) { 
    std::cout << "stuff Derived" 
       << "\n"; 
    } 
}; 

int main(int argc, char *argv[]) { 
    std::shared_ptr<Base> b1(new DerivedA()); 
    std::shared_ptr<Base> b2(new DerivedB()); 

    b1->stuff(b2); 
    b2->stuff(b1); 
    return 0; 
} 

Der Ausgang wird sein:

stuff Base 
stuff Base 

Nun, ich nehme an, dass es nicht möglich ist, die abgeleitete Methode aufzurufen, da sie in der Basisklasse nicht existiert.

Meine Frage ist: Gibt es eine Möglichkeit, stuff(std::shared_ptr<DerivedA> b) mit der Basisklasse aufzurufen?

[EDIT]

Ich dachte schon über das Besuchermuster (es soll gesagt haben, und genauer sein).

Meine Klassen repräsentieren Entitäten und ich muss Kollisionen zwischen ihnen überprüfen. Jedoch wird eine Kollision zwischen A & B eine andere Wirkung haben als zwischen

Ich stimme zu, dass es funktioniert, aber es bedeutet, dass ich Tonnen von Methoden definieren muss.

Gibt es einen eleganteren Weg, es zu tun?

Vielen Dank im Voraus.

+0

Das sieht nach einem Job für das Besuchermuster aus! https://en.wikipedia.org/wiki/Visitor_pattern – Louen

+3

Sieht so aus, als ob Sie zwischen dem Überladen der Methode (oder einer Funktion) und dem Überschreiben der (virtuellen) Methode verwechselt werden. – hyde

+0

Ich habe meine Frage bearbeitet, um @Louen zu beantworten – Luc

Antwort

1

Was Sie suchen ist häufig mehrere Dispatch oder eine Multimethode genannt. In C++ gibt es hierfür keine direkte Sprachunterstützung, Sie können sie jedoch explizit selbst implementieren. Grundsätzlich haben Sie eine virtuelle Funktion, die mit einem konkreten Objekt in einer anderen virtuellen Funktion entsendet:

struct DerivedA; 
struct DerivedB; 

struct Base { 
    virtual ~Base() = default; 
    virtual void stuff(shared_ptr<Base>) = 0; 

    virtual void dispatch_stuff(Base&) = 0; 
    virtual void dispatch_stuff(DerivedA& p) { return dispatch_stuff(static_cast<Base&>(p)); } 
    virtual void dispatch_stuff(DerivedB& p) { return dispatch_stuff(static_cast<Base&>(p)); } 

struct DerivedA : Base { 
    void stuff(shared_ptr<Base> rhs) override { 
     rhs->dispatch_stuff(*this); 
    } 

    void dispatch_stuff(Base&) { /* default */ } 
    void dispatch_stuff(DerivedA&) { /* specific A-A stuff */ } 
}; 

Auf diese Weise:

b1->stuff(b2); // calls b2->dispatch_stuff(DerivedA&) 
b2->stuff(b1); // calls b1->dispatch_stuff(DerivedB&) 
1

Meine Frage ist: Gibt es eine Möglichkeit, stuff(std::shared_ptr<DerivedA> b) mit der Basisklasse aufzurufen?

Nein, weil die Klassenschnittstelle Base die Methode, die Sie aufrufen möchten, nicht implementiert. Obwohl sich der Klassenzeiger Base auf ein -Objekt bezieht und Sie die Methode im Hinblick auf den vom Zeiger angegebenen Typ des Objekts (dh DerivedB) durch Poilformism auflösen können, können Sie nur die in der Klasse Base definierte Methode aufrufen . Daher können Sie nicht stuff(std::shared_ptr<DerivedA> b) mit einem Base Zeiger aufrufen, der auf ein Objekt zeigt.

Zum Beispiel:

std::shared_ptr<Base> b1(new DerivedA()); 
    std::shared_ptr<Base> b2(new DerivedB()); 
    std::shared_ptr<DerivedA> a1(new DerivedA()); 
    std::shared_ptr<DerivedB> bb1(new DerivedB()); 

    b1->stuff(b2); 
    b2->stuff(b1); 
    b2->stuff(a1); // b2 is the only class that implement stuff(std::shared_ptr<DerivedA> b) 
    bb1->stuff(a1) 

Ausgang:

stuff Base 
stuff Base 
stuff Base 
stuff Derived 
stuff Base 
1

Das Problem, das Sie zu lösen versuchen, das double dispatch Problem genannt wird, was bedeutet, Sie versuchen, ein Verhalten abhängig vom konkreten Typ von zwei Objekten aufzurufen. Wenn Sie diesen Begriff bei Google oder hier nachschlagen, können Sie interessante Ergebnisse erzielen.

Die erste Sache, auf die eine oder andere Weise, werden Sie eine Menge Funktionen schreiben müssen, denn wenn Sie N verschiedene Typen haben, gibt es N N mögliche Paarungen. (N N/2, wenn die Reihenfolge keine Rolle spielt, was wahrscheinlich in Ihrem Kollisionsszenario der Fall ist).

Die visitor pattern ist eine der kanonischen Lösungen für das doppelte Versandproblem.

Es gibt andere, je nachdem, was Ihnen wichtig ist. Wenn zum Beispiel die Anzahl der Subtypen zum Zeitpunkt der Kompilierung begrenzt und bekannt ist, können Sie zum Beispiel einen Index für jeden Typ und ein 2D-Array von Funktionszeigern aufrufen (nicht sehr elegant oder sehr objektorientiert) aber ziemlich effizient in Bezug auf die Leistung).

Wenn Sie befürchten, dass die Anzahl der Funktionen Codeduplikation verursachen kann, können Sie den Code immer in eine Funktion oder eine Klasse einbeziehen (etwa CollisionPairBehavior).

Verwandte Themen