2017-06-01 2 views
0

ich mit Polymorphismus am Experimentieren und boost :: Variante in C++ 11boost :: variant und Polymorphismus in C++ 11

Hier ist der Code

#include <iostream> 
#include <boost/variant.hpp> 
using namespace std; 

class Polygon { 
protected: 
     int width, height; 
public: 
     void set_values (int a, int b) 
     { 
       width=a; 
       height=b; 
     } 
}; 

class Rectangle: public Polygon { 
public: 
     Rectangle() { 
       std::cout << "ctor rectangle" << std::endl; 
     } 

     int area() 
     { 
       return width*height; 
     } 
}; 

class Triangle: public Polygon { 
public: 
     Triangle() { 
       std::cout << "ctor triangle" << std::endl; 
     } 
     int area() 
     { 
       return width*height/2; 
     } 
}; 


int main() { 

     Triangle r; 
     boost::variant<Rectangle, Triangle> container = r; 
     int x = 4; 
     int y = 5; 
     if (container.type() == typeid(Rectangle)) { 
       r.set_values(x,y); 
       std::cout << r.area() << std::endl; 
     } else if (container.type() == typeid(Triangle)){ 
       r.set_values(x,y); 
       std::cout << r.area() << std::endl; 
     } 

     return 0; 


} 

Ich frage mich, ob dies der beste Weg, um fortzufahren. Es gibt eine Wiederholung im Code (in der main() -Funktion), wo wir für jeden Typ (wir erhalten den Typ zur Laufzeit) dasselbe ausführen, dh den Wert einstellen und den Bereich drucken.

Gibt es einen besseren Weg, dies zu tun?

+2

Warum fügen Sie nicht 'virtual int area()' in 'Polygon' ein? Ich sehe keinen Bedarf für "Variante" hier; regulärer Polymorphismus würde in diesem Beispiel gut funktionieren. – 0x5453

+3

Auch RTTI ist generell eine schlechte Idee und sollte bei Verwendung von 'variant' nicht benötigt werden. Sie möchten stattdessen einen Besucher verwenden. – 0x5453

+1

Wenn die Frage ist, "wie Wiederholung im Code zu beseitigen", scheint die Frage in Ordnung. Aber Ihre abschließende Aussage sagt "Gibt es einen besseren Weg, dies zu tun?", Die zu weit/unklar ist. Wenn du meinst "Wie rufe ich eine Methode einer' boost :: variant' "auf, ist es auch in Ordnung (ohne Duplikate).Sei klar über deine Frage –

Antwort

0

Verwenden Sie keine if-else-Konstrukte.

Werfen Sie einen Blick auf boost. Ich tippte unten ein kleines und ungetestetes Beispiel.

#include "boost/variant.hpp" 
#include <iostream> 

class my_visitor : public boost::static_visitor<void> 
{ 
public: 
    void operator()(Rectangle const & i) const 
    { 
     // do something here 
    } 

    void operator()(Triangle const & i) const 
    { 
     // do something here 
    } 
}; 

int main() 
{ 
    boost::variant< Triangle, Rectangle > u(Triangle()); 
    boost::apply_visitor(my_visitor(), u); 
} 
+0

Ähm, das wird nicht bauen, oder zumindest sollte es nicht. Ihr Besucher gibt ein 'int' zurück, das an 'auto &' nicht erlaubt ist. – Yakk

+0

Es ist ungeprüft und schnell Code, es ist nicht perfekt, aber gut genug zu verstehen. – user1587451

+1

Es sieht grundsätzlich fehlerhaft aus? Sie können nicht zwei verschiedene Typen von einem abweichenden Besucher zurückgeben. Und keiner konvertiert zu int. Und es verstößt gegen const. Ich bin mir nicht sicher, was Sie versuchen, und ich kann einen abweichenden Besucher schreiben. Es gibt 9 Zeilen tatsächlichen Codes, und ungefähr die Hälfte von ihnen enthält oder trägt dazu bei, Zeitfehler zu kompilieren. – Yakk

1

Dies ist eine Hilfsklasse für den Fall, dass Sie Werttyp-Varianten-basierten Polymorphismus wünschen.

template<class Base> 
struct poly_ptr_t:boost::static_visitor<Base*> { 
    template<class T> 
    Base* operator()(T& t)const { return std::addressof(t); } 

    template<class...Ts> 
    Base* operator[](boost::variant<Ts...>& v) const { 
    return boost::apply_visitor(*this, v); 
    } 
    template<class...Ts> 
    Base const* operator[](boost::variant<Ts...> const& v) const { 
    return boost::apply_visitor(*this, v); 
    } 
}; 

Verwendung:

poly_ptr_t<Polygon> as_polygon; 
int main() { 
    boost::variant<Triangle, Rectangle> u(Triangle{}); 
    as_polygon[u]->set_values(x,y); 
} 

Nun ist area ein bisschen Schmerz. Das Erlangen des Elternteils Polygon wird nicht helfen, weil es keinen Bereich hat.

Wenn wir hinzugefügt

virtual int area() = 0; 

zu Polygon dann

std::cout << as_polygon[v]->area(); 

arbeitet plötzlich.

Die Alternative ist in C++ 11 ein wenig durcheinander. In C 14 mit entsprechender boost Unterstützung ++, erhalten wir:

std::cout << boost::apply_visitor([](auto& e){return e.area();}, v); 

oder

boost::apply_visitor([](auto& e){std::cout << e.area();}, v); 

wo wir eine generische Lambda verwenden area zu nennen.

Oder wir können eine Fläche Besucher schreiben:

struct get_area:boost::static_visitor<int> { 
    template<class T> 
    int operator()(T& t)const{ return t.area(); } 
}; 

jetzt wir dies tun können:

std::cout << boost::apply_visitor(get_area, v); 

In keinem dieser Fälle haben wir den Code Wiederholung innerhalb des Haupt haben.