2016-08-23 3 views
1

Angenommen, eine Domain, die einen Zeiger auf Shape speichert. Die genaue Form (Triangle oder Rectangle) ist zur Kompilierzeit nicht bekannt und wäre nach dem Lesen der Eingabe klar. Zur Laufzeit muss ich möglicherweise auf Variablen abgeleiteter Strukturen zugreifen, aber dies ist nicht möglich, da der Zeiger auf die Basisstruktur zeigt. Ich fand eine andere Lösung, die "Einschalt-Typ" zu tun, aber es wird abgeraten, wie in der Antwort here hingewiesen. Er sagte auch, dassGute Praxis der Polymorphie

Wenn Sie Polymorphie verwenden, sollten Sie sich nicht kümmern, was hinter einer Basisklasse Verweis/Zeiger ist.

Nun in diesem Fall ist mir egal, so klingt es wie ich sollte nicht Polymorphie verwenden. Ich denke, was ich unten mache, ist ein schlechtes Design, aber was ist dann ein guter Entwurf, um dieses Problem zu lösen?

struct Shape 
{ 
    int common_variable; 
}; 

struct Triangle: Shape 
{ 
    int triangle_specific_variable; 
}; 

struct Rectangle: Shape 
{ 
    int rectangle_specific_variable; 
}; 

struct Domain 
{ 
    Shape* shape; 
}; 

int main() 
{ 
    Domain domain; 
    //domain.shape = new Triangle(); // depends on input. 
    //domain.shape = new Rectangle(); // depends on input. 

    return 0; 
} 
+0

ich nicht ganz Ihre Frage oder Ihr Beispiel verstehen.Es scheint, dass ein polymorpher Ansatz hier nützlich sein könnte, aber ich verstehe nicht genug von dem, was Sie tun wollen, um helfen zu können. – callyalater

+0

@callyalater: Ich muss auf abgeleitete Struktur spezifische Variablen zugreifen, aber ich kann nicht, weil der Zeiger auf die Basisstruktur zeigt. – Shibli

+0

Wenn Sie auf Member der 'Derived'-Struktur über' Base'-Pointer zugreifen wollen, können Sie 'dynamic_cast' verwenden, aber Sie müssen die polymorphe Klasse 'Base'-Klasse erstellen, zum Beispiel mit Dummy' virtual void f() { } 'Funktion. – PcAF

Antwort

6

Ihre Frage zeigt deutlich, für Polymorphismus benötigen, wie Sie mit Dreiecke arbeiten möchten, rechtangles usw., und Sie wissen, dass alle diese Formen sind.

Warum wird nicht empfohlen, mit einem Switch auf bestimmte Daten zuzugreifen?

Weil dies genau das Gegenteil eines polymorphen Designs ist. Anstatt mit Formen zu arbeiten, sie zu zeichnen, ihren Bereich zu berechnen, usw., müssen Sie immer den Typ des Form- und Code-spezifischen Verhaltens kennen.

Stellen Sie sich vor, Sie haben Ihren Code abgeschlossen, und dann entdecken Sie, dass Sie auch Quadrate und Kreise wollen: Was für ein Albtraum wäre es, dies zu erhalten.

Wie löst man das?

Sie müssen aus den konkreten Klassen abstrahieren und die allgemeinen Operationen definieren, die Sie an allgemeinen Formen ausführen können. Definieren Sie diese Operationen dann als virtuelle Funktionen, und rufen Sie in Ihrem Code mit Domain die virtuellen Funktionen auf.

Um weiter zu generalisieren, können Sie, anstatt Objekte aus der Klasse zu erstellen, eine Factory-Methode verwenden, die beispielsweise eine Form aus einem Stream zurückgibt.

Beispiel:

class Shape 
{ 
public: 
    virtual void scale(double scale_factor)=0; 
    virtual void rotate(double angle)=0; 
    virtual void draw(window w)=0; 
    virtual Shape* clone()=0; 
    virtual ~Shape() {} 
}; 

class Triangle: public Shape 
{ 
    Point A, B, C; 
public: 
    Triangle (Point a,Point b, Point c) : A(a), B(b), C(c) { } 
    void scale(double scale_factor) override; 
    void rotate(double angle) override; 
    void draw(window w) override; 
    Shape* clone() { return new Triangle(*this); }  
}; 

... 

int main() 
{ 
    Domain domain, domain2; 
    Window wnd = CreateWindow(...); // depends on your GUI library 
    Point a,b,c; 
    cin >> a >> b >> c; 
    domain.shape = new Triangle(a,b,c); 

    // Once the object is constructed no need to know the details of the shape here 
    domain.shape->rotate(45); 
    domain2.shape = domain.shape->clone(); 
    domain.shape->draw(wnd); 
    ... 
    return 0; 
} 

Beachten Sie, dass mit intelligenten Zeigern arbeiten wäre sicherer als mit rohen Zeiger;

0

Zuerst. Polymorphie ist fast immer nur das richtige Werkzeug für den Job, wenn die konkreten Objekte genau die gleiche Schnittstelle haben.

Zweitens, das ist selten der Fall und manchmal Polymorphismus ist eine bessere Passform als eine Variante.

In einer solchen Situation verschieben wir das Verhalten auf die Implementierung, anstatt zu versuchen, es von außerhalb des Objekts abzuleiten.

Eine Möglichkeit, dies zu tun, ist das ‚Besuchermuster‘:

struct triangly_thing { int i; }; 
struct rectangly_thing { int i; }; 

struct shape_visitor 
{ 
    virtual void operator()(triangly_thing thing) const 
    { 
     // do triangly things 
    } 

    virtual void operator()(rectangly_thing thing) const 
    { 
     // do rectangly things 
    } 
}; 

struct Shape 
{ 
    int common_variable; 

    virtual void visit(shape_visitor const& visitor) 
    { 

    } 
}; 


struct Triangle: Shape 
{ 
    triangly_thing my_triangly_thing; 

    void visit(shape_visitor const& visitor) override 
    { 
     visitor(my_triangly_thing); 
    } 
}; 

struct Rectangle: Shape 
{ 
    rectangly_thing my_rectangly_thing; 

    void visit(shape_visitor const& visitor) override 
    { 
     visitor(my_rectangly_thing); 
    } 
}; 

struct Domain 
{ 
    Shape* shape; 
}; 

int main() 
{ 
    Domain domain; 

    struct my_shape_visitor_type : shape_visitor 
    { 
     // customise overrides in here 
    } my_shape_visitor; 

    domain.shape->visit(my_shape_visitor); 

    return 0; 
}