Vielen Dank für die Antworten!
Wenn ich das richtig verstanden habe, mache ich das auch so. Ich habe nur sehr wenige Stellen, wo ich z.B. Mehrfachvererbung nutze/benötige, wodurch dann aber keine bestehenden Funktionen verändert/überschrieben werden.
dot hat geschrieben:Goderion hat geschrieben:Ich benutze in meinem Programm für fast alle Objekte folgendes Interface (sehr ähnlich wie IUnknown von OLE/COM):
wieso?
Ursprünglich habe ich das nur wegen dem Referenzzähler gemacht, mittlerweile habe ich aber weitere Funktionen implementiert, die mir das Überwachen und Prüfen der Objekte vereinfachen.
dot hat geschrieben:Goderion hat geschrieben:Gibt es da keine Möglichkeit, das Ganze so zu gestalten, dass man nicht für jede Klasse die nötige Funktion (GetObjectTypeId/GetObjectTypeName) implementieren muss?
Es geht sogar noch besser: Man gestaltet das Ganze so, dass man überhaupt kein
GetTypeIrgendwas() braucht.
Bis jetzt habe ich das auch immer geschafft, diese Funktion ausschließlich für Analyse-Zwecke zu nutzen, aber für mein aktuelles Vorhaben/Problem weiß ich nicht, wie ich das sinnvoll ohne diese Funktion lösen soll.
dot hat geschrieben:Goderion hat geschrieben:1. Ich habe eine grafische Benutzeroberfläche, wo ich jedem Fenster (ClassGuiWindow) eine Fenster-Klasse (ClassGuiWindowClass) zuweisen kann. Es gibt Situationen, wo ich z.B. alle Kind-Fenster von einem Fenster durchgehen muss um die Fenster mit einer speziellen Fenster-Klasse weiter zu behandeln.
Inwiefern hilt die Abfrage des konkreten Objekttyps dabei?
Wie soll ich sonst die Fenster ermitteln, die z.B. gerade die Fenster-Klasse zum Darstellen von Container-Inhalten (Truhe, Beutel, Schrank) benutzen?
Ich will z.B. durch einen Doppelklick auf der Karte den Inhalt von einem Schrank anzeigen. Bevor ich ein neues Fenster erstelle, durchsuche ich die bereits vorhandenen Fenster, ob der Schrank nicht schon angezeigt wird.
Ich könnte der Fenster-Klasse einfach eine virtuelle Funktion mitgeben, womit man das prüfen könnte, und nur die betroffenen Fenster-Klassen implementieren das.
Aber wenn ich das mache, habe ich am Ende hunderte virtueller Funktionen, was ich sehr unübersichtlich finden würde. Ah... jetzt habe ich vielleicht ein Idee, da muss ich erstmal drüber nachdenken. ;-)
dot hat geschrieben:Goderion hat geschrieben:2. Beim Drag&Drop kann auch ein Object (InterfaceObjectBase*) genutzt werden. ich muss aber während dem "Draggen" und dann beim "Droppen" natürlich wissen, was das für ein Objekt ist.
https://en.wikipedia.org/wiki/Double_dispatch
Mal ein Beispiel:
Ich habe eine Karte offen. In der Karte werden 2 Truhen und 2 Charakter jeweils per Fenster mit entsprechender Fenster-Klasse angezeigt. Dazu ist noch eine Minimap offen und ein Textfenster.
Jetzt gehe ich mit der Maus in der Karte auf z.B. einen Helm und fange an den per Drag&Drop zu bewegen.
Wenn ich den Helm über ein Fenster ziehe, werden dadurch entsprechende Funktionen in der Fenster-Klasse aufgerufen.
Die Fenster-Klasse muss jetzt wissen, was ich da gerade mit der Maus bewege, um entsprechend reagieren zu können.
Auf der Karte wird der Helm z.B. wie auch beim Bewegen von Icons auf dem Desktop an der Maus-Position leicht transparent angezeigt. Gehe ich damit allerdings über das Textfenster oder Minimap, erscheint eine Warnung per Tooltip. Im Charakter-Fenster kommt es dann z.B. darauf an, über welchen Ausrüstungs-Slot ich den Helm bewege.
Spontan fallen mir nur Möglichkeiten ein, ohne dabei den Objekttyp zu erfragen, die viel zu umständlich sind und/oder das Ganze unübersichtlich machen.
NytroX hat geschrieben:Funktioniert! Aber erst nachdem ich in ClassTest eine virtuelle Funktion hatte
Oha, kann es sein, dass dein Destructor in der Basisklasse/dem Interface dann nicht virtual ist?
Im Basisinterface gibt es folgende Funktion:
Jedes Objekt kümmert sich dann um die eigene Löschung, was einen virtuellen Destruktor überflüssig macht.
Am Anfang hat mich das etwas genervt, für jedes Objekt extra eine Delete-Funktion zu implementieren.
Aber im Nachhinein hat sich das als extrem nützlich erwiesen, vor allem in Kombination mit einem eigenen Speichermanager.
Jetzt habe ich mal eine Verständnisfrage zum folgenden Quellcode:
Code: Alles auswählen
class InterfaceBase
{
public:
virtual void AddRef(void) = 0;
virtual void Release(void) = 0;
};
class ClassBase : public InterfaceBase
{
public:
ClassBase(void)
{
m_RefCount = 0;
}
void AddRef(void)
{
++m_RefCount;
}
void Release(void)
{
--m_RefCount;
if (0 == m_RefCount)
{
Delete();
}
}
private:
virtual void Delete(void) = 0;
int m_RefCount;
};
class ClassTest : public ClassBase
{
public:
private:
void Delete(void)
{
delete this;
}
};
ClassTest* pTest = new ClassTest();
// hier wird unnötigerweise der vtable genutzt, warum?
pTest->AddRef();
Warum wird trotzdem beim Aufruf von AddRef/Release der vtable genutzt? Der Compiler sollte doch wissen, dass hier nur die Funktionen von ClassBase aufgerufen werden können.