2016-05-15 3 views
1

Ich habe gesucht, bevor Sie Fragen, aber din't nichts gefunden für mein Problem arbeiten.C++ - Machen Sie Zeiger auf Superklasse Match Unterklasse Argument in Funktion

Ich möchte einen Zeiger auf die Oberklasse (die immer auf eine der Unterklassen verweist) machen ein Unterklassenargument (Zeiger oder Const-Verweis) in einer Funktion.

Kontext: Erstellen Sie einen "fortgeschrittenen" Rechner in C++.

Lassen Sie mich Ihnen weitere Informationen zu den Klassen in dieser Ausgabe von mir verwendet:

Zuerst haben wir die Literale:

class Literal {}; 
class IntegerLiteral : public Literal {}; 
class RealLiteral : public Literal {}; 
class RationalLiteral : public Literal {}; 
//.... 

Wir haben einen Stapel verwendet, um die wörtlichen Objekte zu speichern, indem die Speicherung ihres Adressen

// If st is instance of stack then : 
st.top(); // returns a Literal* 

Und wir Operator Objekte, die mit dem Stapel durch Entstapeln die richtigen Zahlen von Wörtliche * (abhängig von der Bedienungs arity) interagieren, Anwenden des Operators auf die Literal * -Objekte und schließlich Stapeln des Ergebnisses.

class Operator { 
    int n; // operator arity 
public: 
    virtual void executeOperator(stack *st) = 0; // 
}; 

Einer der Operator Unterklasse (zum Beispiel):

class PlusOperator : public Operator { 
public: 
    virtual void execute(StackUTComputer *st) override { 
     Literal* arg1 = st->top(); 
     Literal* arg2 = st->top(); 
     Literal* result = applyOperator(arg1, arg2); 
     st->pop(); st->pop(); 
     st->push(result); 
    } 

    Literal* execute(IntegerLiteral* a, IntegerLiteral* b) { 
     return new IntegerLiteral(a->getValue() + b->getValue()); 
    } 

    Literal* execute(IntegerLiteral* a, RealLiteral* b) { 
     return new RealLiteral(a->getValue() + b->getValue()); 
    } 


    Literal* execute(IntegerLiteral* a, RationalLiteral* b) { 
     return new RationalLiteral(
      a->getValue() + (a->getValue()*b->getDenominator()), 
      b->getDenominator() 
    ); 
    } 

    // ... 
}; 

Mein Ziel hier (durch die Funktion applyOperator Überlastung) ist auf „magische Weise“ der Computer wissen lassen, welche Anruffunktion auf die reale Abhängigkeit Typ von Literal, der vom Operator entstapelt wurde (die Klasse Literal ist abstrakt: Der Stapel enthält immer spezifische Unterklassen von Literal).

Aber es funktioniert nicht so, wie ich will. Ich meine, dass der Aufruf applyOperator(arg1, arg2) (mit arg1 und arg2 als Literal *) ungültig ist, da keine Funktionen mit der Signatur übereinstimmen.

Ich bin mir bewusst, dass ich den C++ - Polymorphismus auf die andere Art und Weise verwende, die normalerweise verwendet wird (das ist ein Unterklassenargument für eine Funktion, die ein Superklassenargument verwendet).

Ich weiß nicht, wie ich meine Architektur umkehren soll, um den Polymorphismus richtig zu benutzen, und vielleicht gibt es eine Syntax-hilfreiche Lösung, um meine Idee zum Laufen zu bringen.

Wie auch immer, ich bin dankbar für Ihre Hilfe !!

Raphael.

+2

Weniger Prosa, konkretere Fehlermeldungen und ein [MCVE] würde sehr helfen, diese Frage zu verbessern. –

+0

Hier gibt es keine Deklaration von "applyOperator". –

Antwort

1

Es gibt einen Weg, um es mit Polymorphismus zu tun, wie soll (ohne dynamic_cast):

#include <iostream> 
#include <memory> 
#include <string> 

struct IntegerLiteral; 
struct RealLiteral; 

struct Literal { 
    virtual void add(const Literal &) = 0; 
    virtual void add(const IntegerLiteral &) = 0; 
    virtual void add(const RealLiteral &) = 0; 

    virtual void add_to(Literal &) const = 0; 
    virtual void add_to(IntegerLiteral &) const = 0; 
    virtual void add_to(RealLiteral &) const = 0; 
    virtual std::ostream &print(std::ostream &os) const = 0; 
    virtual ~Literal() = default; 
}; 

std::ostream &operator<<(std::ostream &os, const Literal &l) { 
    return l.print(os); 
} 

struct IntegerLiteral : Literal { 
    IntegerLiteral(int i) 
     : i(i) {} 
    int i = 0; 
    void add(const Literal &other) override { 
     //now we know one operand is an IntegerLiteral and can pass on that information to the other Literal 
     other.add_to(*this); 
    } 
    void add(const IntegerLiteral &other) override { 
     i += other.i; 
    } 
    void add(const RealLiteral &other) override; 
    void add_to(Literal &other) const override { 
     other.add(*this); 
    } 
    void add_to(IntegerLiteral &other) const override { 
     other.i += i; 
    } 
    void add_to(RealLiteral &other) const override; 
    std::ostream &print(std::ostream &os) const override { 
     return os << i; 
    } 
}; 

struct RealLiteral : Literal { 
    RealLiteral(double d) 
     : d(d) {} 
    double d = 0; 
    void add(const Literal &other) override { 
     other.add_to(*this); 
    } 
    void add(const IntegerLiteral &other) override { 
     d += other.i; 
    } 
    void add(const RealLiteral &other) override { 
     d += other.d; 
    } 
    void add_to(Literal &other) const override { 
     other.add(*this); 
    } 
    void add_to(IntegerLiteral &other) const override { 
     //now we know both operands and can do the calculation 
     other.i += d; 
    } 
    void add_to(RealLiteral &other) const override { 
     other.d += d; 
    } 
    std::ostream &print(std::ostream &os) const override { 
     return os << d; 
    } 
}; 

void IntegerLiteral::add(const RealLiteral &other) { 
    i += other.d; 
} 

void IntegerLiteral::add_to(RealLiteral &other) const { 
    other.d += i; 
} 

int main() { 
    std::unique_ptr<Literal> l1 = std::make_unique<RealLiteral>(3.14); 
    std::unique_ptr<Literal> l2 = std::make_unique<IntegerLiteral>(42); 
    l1->add(*l2); 
    std::cout << *l1 << '\n'; 
} 

DEMO

Sie benötigen eine Tonne Code, um diese Arbeit zu machen, und es wird quadratisch schlimmer mit jeder Literal Sie addieren und doppelt so schlecht mit jedem Operator. Auch wenn Sie override eine Funktion vergessen, erhalten Sie wahrscheinlich eine Endlosschleife und einen Stapelüberlauf zur Laufzeit.
Ein viel besserer Ansatz (einfacher zu schreiben und schneller zu laufen) wäre, einfach double oder BigNum für alles zu verwenden und sich nicht mit Polymorphie zu beschäftigen.

+0

Vielen Dank, dass Sie sich die Zeit genommen haben, diese Demo zu schreiben. Ich sehe jetzt die schlechte Architektur, die ich implementieren wollte. Ich werde es ändern, um einfach 'double' für alles zu verwenden. – raphael

0

Sie mischen verschiedene Konzepte, die ad hoc polymorphism (Überladungen) und subtype polymorphism sind, was durch späte Bindung von Methoden über virtuelle Tabellen implementiert wird.

Grundsätzlich wählt der Compiler, welche überladene Methode zur Kompilierzeit aufgerufen werden soll, nicht zur Laufzeit. Dies macht unmöglich, was Sie ohne RTTI tun möchten.

Der Compiler ist nicht in der Lage zu bestimmen, welche der beiden Literal Instanzen sein wird, und C++ unterstützt nur frühe Bindung beim Umgang mit nicht virtuellen Methoden. Das einzige, was zur Kompilierzeit abgeleitet werden kann, ist die Tatsache, dass beide Argumente vom Typ Literal* sind, so dass nur nach dieser Überladung gesucht wird.

Sie benötigen eine Art dynamischer Schalter, um zu tun, was Sie wollen, die durch die Verwendung von dynamic_cast (oder ähnliche handgemachte Lösungen) erhalten werden kann.

+0

Vielen Dank für Ihre Erklärungen. Ich sehe jetzt klar, warum der Compiler nicht in der Lage war zu bestimmen, was zu tun ist. – raphael

Verwandte Themen