virtual get() und non-virtual set() möglich?
-
Hallo,
ich habe folgendes Problem: Ich habe eine Klassenhierarchie mit geometrischen Objekten, die etwa so aussieht:
class Item { public: Item( Item* parent = 0); [...] void setAccessible(bool b = false); [...] virtual bool isAccessible() const; [...] private: bool mAccessible; [...] }; class GeoItem : public Item { public: GeoItem( Item* parent = 0); [...] virtual bool isAccessible() const [...] }; class Floor : public GeoItem { public: Floor( Item* parent = 0); [...] virtual bool isAccessible() const; [...] }; Item::Item( Item* parent ) { [...] setAccessible(); } bool Item::isAccessible() const { return mAccessible; } Floor::Floor( Item* parent ) { [...] setAccessible( true ); } bool Floor::isAccessible() const { return mAccessible; }Die Eigenschaft
mAccessible
soll also in der Basisklasse definiert werden und default-mässig mit
false
belegt werden. Nur bei erreichbaren Items wie
Floor
soll die Eigenschaft gesetzt werden.
In einer Liste habe ich dann Zeiger auf GeoItems (Qt-Code, foreach iteriert über die Liste):
QList< GeoItem* > list; foreach( GeoItem* g, list ) { if g->isAccessible() { // tue dies } }Ist es möglich ein Element einer Basisklasse zu definieren und mit einer nicht virtuellen Funktion zu setzen, aber mit einer virtuellen Funktion abzufragen (get...) ?
Eigentlich wäre es noch besser, wenn mAccessible statisch wäre, da dies für alle Objekte eines entsprechenden Typs gelten muss, aber virtuell und static ist in C++ leider nicht möglich?
Danke für alle Hinweise,
AlGaN
-
Für was sollte virtual UND static auch gut sein? Mach doch eine static in der Basisklasse und lass es gut sein.
Oder definier die get/set nicht virtuell, und implementier sie in etwa so:bool Item::get() { if( parent() ) { return parent()->get(); } return m_val; } void Item::set( bool val ) { if( parent() ) { parent()->set( val ); return; } m_val = val; }Aber irgendwie nehm ich an dass das QGraphicsItems sind, oder?

Da kannst du doch sicher auch was mit den GraphicsItemFlags anfangen, oder?
Was hast du denn genau vor, bzw. wie zeichnet sich "accessible" aus?
-
Ist es möglich ein Element einer Basisklasse zu definieren und mit einer nicht virtuellen Funktion zu setzen, aber mit einer virtuellen Funktion abzufragen (get...) ?
Warum sollte das nicht möglich sein? Du musst nur mAccessible protected machen, sonst darfst Du in den abgeleiteten Klassen nicht mehr darauf zugreifen. Aber Du denkst viel zu kompliziert. isAccessible() macht in allen Deinen abgeleiteten Klassen das Gleiche, da brauchst Du gar keine virtuelle Funktion.
Eigentlich wäre es noch besser, wenn mAccessible statisch wäre, da dies für alle Objekte eines entsprechenden Typs gelten muss, aber virtuell und static ist in C++ leider nicht möglich?
Ja statisch und virtuell passt nicht zusammen. Das was Du willst, geht aber viel einfacher. Dur brauchst keine Funktion setAccesible() und auch kein Member mAccessible in Item. Du brauchst nur die virtuelle Funktion:
class Item { public: virtual bool isAccessible() const; } bool Item::isAccessible() const { return false; }Klassen die "zugreifbar" sein sollen, überschreiben die Funktion einfach:
class Floor : public Item { public: virtual bool isAccessible() const; }; bool Floor::isAccessible() const { return true; }
-
Hallo,
@DJohn: Ja, hab auch gemerkt, dass ich da einen "Knoten" im Kopf hatte, es ist eine total simple Eigenschaft eines Objekts, die gesetzt/abgefragt werden kann -> kein Grund für virtual...
Der Hauptgrund, dass ich die Eigenschaft mAccessible eingeführt hatte, statt alles hart zu kodieren, war das evt. später mal die Anforderung dazukommt, ein Objekt nach seiner Erzeugung "umzuschalten" (also z.B. von "nicht zugänglich" auf "zugänglich" setzen)...
@franz: Nein, es sind keine QGraphicsItems, obwohl das eine gute Alternative wäre, das Ganze umzuschreiben, das QGraphicsView-Framework kam bei Qt erst nach meiner ursprünglichen Entwicklungsphase hinzu, so dass die Objekte bisher noch "von Hand" gezeichnet werden...
"accessible" soll einfach anzeigen, ob ein Objekt (in diesem Fall ein "Boden"-Objekt) begehbar/zugänglich ist, so dass ich die nicht begehbaren Objekte auf Kollision testen kann...
Danke nochmals für alle Tipps,
AlGaN
-
Static wird für dich sicherliche keine Alternative sein! Denn das bedeutet, entweder ist alles begehbar oder nichts?
Und da du sicherlich einzelne Bereiche begehen können willst (wirds ein Spiel?) ist wohl die von mir gezeigte Lösung (also die parents durchwandern) sicherlich die einzige Lösung.Natürlich solltest du noch 2 Fälle abfangen:
- Wenn das parent item begehbar ist, geh weiter in der Rekursion (also return parent()->isAccessible(); )
- Wenn das parent NICHT begehbar ist -> return false; und Rekursion abbrechen.
Rekursion nur so lange bis das parent() NULL ist, also das root item erreicht wurde.
Damit solltest du per default m_accessible als true setzen, und explizit auf false setzen, wenn es notwendig ist (Alternativ einen Zweiten Parameter in den Konstruktor, mit dem du gleich accessible setzen kannst.
Eine Alternative wäre, dass du in Item ein enum für den Typ setzt. Dann kannst du an eine statische Methode denken, die prüft ob dieser Typ begehbar ist. So könntest du auf set/isAccessible vollständig verzichten.
Aber prinzipiell musst du schon die parents auch prüfen.
Ein Holzbrett auf dem Boden ist begehbar (da Floor begehbar ist), hingegen ein Holzbrett auf einem Schrank ist nicht begehbar.Grüße
Franz// edit
Z.B. kann das so ausschauen:Item::Item( Item* parent=0 ) : m_parent(parent), m_accessible(true) { } bool Item::isAccessible() { if( !m_accessible ) { return false; // wenn explizit als nicht begehbar deklariert } // weiter, wenn m_accessible true if( m_parent ) { return m_parent->isAccessible(); // Falls parent existiert parent nach Begehbarkeit fragen. } // weder wurde accessible auf false gesetzt // noch gibt es ein parent (this == rootItem) return true; }
-
Hallo franz,
ja, den Ansatz mit static hab ich inzwischen auch verworfen, damit man die Items individuell setzen kann.
Das mit der rekursiven Abfrage hab ich bis jetzt noch gar nicht bedacht, danke für den Hinweis. Wobei ich mich gerade frage, ob es für den Setter richtig ist, z.B. wenn ich folgende Hierarchie habe:
Building / \ Room Room / \ \ Floor Wall [...]Bei der Abfrage auf Begehbarkeit ist die Rekursion richtig: Floor ist als begehbar gesetzt, also muss ich checken, ob Room insgesamt begehbar ist.
Aber beim Setzen der Eigenschaft mittels dem Code:
void Item::set( bool val ) { if( parent() ) { parent()->set( val ); return; } m_val = val; }im Fall von Wall setze ich mAccessible auf false, aber das heißt ja noch nicht, dass der übergeordnete Room ebenfalls nicht begehbar ist? Bin da grad etwas verwirrt
?//edit:
Ist es nicht eher so, dass ich beim Setzen der Eigenschaft die Kinder des Items rekursiv setzen muss ? z.B. so:void Item::set(bool val) { if ( this->hasChildren() ) { foreach(Item* i, m_Children) { i->set( val ); } } m_val = val; }Danke für alle Hinweise,
AlGaN
-
AlGaN schrieb:
Aber beim Setzen der Eigenschaft mittels dem Code:
void Item::set( bool val ) { if( parent() ) { parent()->set( val ); return; } m_val = val; }im Fall von Wall setze ich mAccessible auf false, aber das heißt ja noch nicht, dass der übergeordnete Room ebenfalls nicht begehbar ist? Bin da grad etwas verwirrt
?Du darfst doch das übergeordnete item gar nicht setzen. Wenn du Wall auf false setzt, wird das in isAccessible abgefangen und der parent-Durchlauf gestoppt.
void Item::setAccessible( bool val ) { m_accessible = val; }Und in den Abgeleiteten Klassen musst du ja gar nix mehr implementieren (nur so nebenbei). isAccessible und setAccessible werden nur in Item implementiert.
Ich hoffe das ist jetzt klarer

Im übrigen wollte ich noch erwähnen, dass allein schon wegen dem Collision.detection es sich lohnen würde, deine Application auf das Graphics View Framewok zu portieren, das kommt nämlich automatisch mit, und wird auch durch Qt selbst gewartet
