Polymorpher Zugriff auf Property in Base Class
-
Hallo,
ich hab wiedermal ein Problem mit polymorphen Funktionen...
Eigentlich hab ich gedacht, ich hätte virtuelle Funktionen verstanden, aber mir scheint doch noch nicht alles klar zu sein...
Folgende Klassenhierarchie (QPen = Stiftklasse aus Qt):
class Item { public: Item( Item* parent = 0); [...] void setPen( const QPen& p ); const QPen& pen() const { return mPen; } void setDefaultPen( const QPen& p ); virtual void setDefaultPen(); const QPen& defaultPen() const { return mDefaultPen; } [...] private: Item* mParent; Item* mRoot; QPen mPen; QPen mDefaultPen; [...] }; class Room : public Item { public: Room( Item* parent = 0); [...] }; Item::Item( Item* parent ) : Item( parent ) { setPen( QPen( Qt::NoPen ) ); [...] } void Item::setPen( const QPen& p ) { mPen = p; } void Item::setDefaultPen( const QPen& p ) { mDefaultPen = p; } void Item::setDefaultPen() { mPen = mDefaultPen; }Ich will jetzt in abgeleiteten Klassen wie Room andere Farben verwenden, also z.B. im Konstruktor:
Room::Room( Item* parent ) : Item( parent ) { setPen( QPen( QColor( "red" ) ) ); setDefaultPen( QPen( QColor( "red" ) ) ); [...] }gleichzeitig muss ich jedoch generisch mit einem Item-Zeiger die Property ändern/abfragen:
void foobar() { Item* i = getItemfromList(); // set item pen i->setPen( QColor( "green" ) ); // tue irgendwas i->setDefaultPen(); }Aber da ich nur einen Basiszeiger habe, wird natürlich der Aufruf zu
i->Item::setDefaultPen()mit den in Item gesetzten Properties für mPen und mDefaultPen. Ich möchte aber den mDefaultPen der Room-Klasse setzen.
Wenn o.g. nicht mit Membervariablen möglich ist, muss man es wohl per virtuelle Funktion machen, etwa so:
class Room : public Item { [...] virtual setDefaultPen() { mPen = QPen( QColor( "red" ) ); } [...] };Ich hoffe, dass ich jetzt das Ganze nicht zu verwirrend dargestellt habe.
Danke für alle Hinweise,
AlGaN
-
der defaultPen ist doch in deiner Basisklasse gespeichert, also kann die Basisklasse ihn auch setzen.
Das steht im Room-Konstrutor:
setDefaultPen( QPen( QColor( "red" ) ) );
Damit hast du den gewünschten Wert doch gesetzt. Und in der Basisklasse steht dann nur noch
void setDefaultPen() { mPen = mDefaultPen; }btw: bei solchen Vererbungsstrukturen, wo du polymorphimus nutzt, solltest du die Destruktoren auch virtuell machen!
-
AlGaN schrieb:
...
Noch zwei Hinweise:
1. Keine virtuellen Funktionen direkt oder indirekt im Konstruktor aufrufen (Hierzu sehe ich nicht genügend Code, die Warnung will ich dir aber dennoch geben).
2. Es ist meist besser die Initialisierung von Membern an der Stelle einer Hierarchie zu machen, wo diese deklariert sind. Dies ist keine in Stein gemeißelte Regel, aber erhöht häufig die Wartbarkeit (Reduzierung der Abhängigkeiten). Zumindest würde ich die Initialisierung dann über einen entsprechenden Konstruktor machen.
-
Hallo,
ich hab es jetzt folgendermassen hinbekommen:
class Item { public: Item( Item* parent = 0); virtual ~Item(); void setPen( const QPen& p = QPen( Qt::NoPen ), bool rec = false ); const QPen& pen() const { return mPen; } void resetDefaultPen( bool rec = false ); void setDefaultPen( const QPen& p = QPen( Qt::NoPen ) ) { mDefaultPen = p; } // has to be virtual virtual const QPen defaultPen() const { return QPen( Qt::NoPen ); } [...] private: QPen mPen; QPen mDefaultPen; QList< Item* > mChildren; [...] }; Item::Item( Item* parent ) : Item( parent ) { [...] setPen(); setDefaultPen(); [...] } void Item::setPen(const QPen& p, bool rec) { if ( rec && !mChildren.isEmpty() ) { foreach ( Item* i, mChildren ) { i->setPen(p, rec); } } mPen = p; } void Item::resetDefaultPen(bool rec) { if (rec && !mChildren.isEmpty() ) { foreach ( Item* i, mChildren ) { i->setDefaultPen(rec); } } mPen = defaultPen(); } class Room : public Item { public: Room( Item* parent = 0 ); virtual const QPen defaultPen() const; [...] }; Room::Room( Item* parent ) : Item( parent ) { setPen( QPen( QColor( "red" ) ) ); setDefaultPen( QPen( QColor( "red" ) ) ); [...] } const QPen Room::defaultPen() const { return QPen( QColor( "red" ) ); }Die Methode setDefaultPen(const QPen&) setzt den default Pen eines Items, die Methode resetDefaultPen(bool) setzt den Pen eines Items (optional rekursiv f. alle Unter-Items) wieder auf den default Pen.
Aber, um in dieser Methode für jedes Unteritem die spezielle Farbe zu setzen, muss ich dies mit der virtuellen Methode const QPen defaultPen() const machen, andernfalls wird ja die Methode der Klasse Item aufgerufen, wenn ich nur einen Basisklassenzeiger habe:void foobar() { QList< Item* > myItems = getItemList(); foreach( Item* i, myItems ) { i->setPen( QColor( "blue" ), true ); // set pen recursive for all subitems // tue irgendwas [...] i->resetPen( true ); // reset to default pen for all subitems } }An dieser Stelle bräuchte ich "virtuelle Memberdaten" für den default Pen?
Oder habe ich irgendwo einen Denkfehler??
Bin für alle Hinweise/Tipps dankbar!AlGaN
-
Virtuelle Memberdaten braucht man eigentlich nie. Wenn man in der abgeleiteten Klasse auf Member zugreifen will, die in der Basisklasse noch nicht vorhanden waren, kann man das über eine (möglicherweise rein) virtuelle Funktion umsetzen, die dann je nach Klasse andere Member zurückgibt.
Wenn einem das nicht reicht, ist das Design möglicherweise von Grund auf nicht ganz durchdacht - denn wenn man abgeleitete Klassen gemeinsam über eine Basisklasse durch Polymorphie ansprechen will, sollte man auch gemeinsam vorhandene Operationen ausführen (wobei sich kleine Differenzen in der Funktionalität wie gesagt durch virtuelle Funktionen implementieren lassen).
-
Hallo nexus,
danke für Deine Antwort.
Dass das Design nicht ganz durchdacht ist, vermute ich auch (ich habe die Grundstruktur der Item-Hierarchie auch nicht selbst implementiert, urspr. waren alle Farben für die Items als static definiert, was dazu geführt hatte, dass man die Farbe nur global für alle Items setzen konnte)
Trotzdem fällt mir jetzt auf die Schnelle keine andere Lösung als die in meinem letzten Beitrag ein, kann sein, dass da etwas zu umaständlich gelöst wurde, immerhin funktioniert es so
(das ist im Augenblick das Wichtigste)Vll. werd ich das nochmal überdenken, wenn ich wieder etwas mehr Zeit habe...