doppelte Vererbung
-
jap, ganz so lösen werd ichs nicht, sondern das Design überdenken. Das "Danke!" war mehr auf das virtual public Base bezogen, das kannte ich noch nicht.
greetz KN4CK3R
-
Gugelmoser schrieb:
KN4CK3R schrieb:
...
Hast du dir das
Diamond-Problemwenigstens zu Herzen genommen?Der Weg über Interfaces ist doch okay. Das ist gerade das Mittel der Wahl um Mehrfachvererbung ohne die Nachteile zu ermöglichen. Siehe zum Beispiel auch Java.
-
eine mir zusagende Lösung hab ich immer noch nicht gefunden...
Ich erläutere mal die aktuelle Problemstellung.
Ich hab im Spiel mehrere Spieler, einer davon ist der lokale Spieler. Bei diesem lokalen Spieler habe ich mehr Methoden als bei den anderen (readonly) Spielern (z.B. bewegen o.ä.), ist also quasi eine Erweiterung der normalen Spieler.Im Code habe ich nun folgendes Konstrukt:
ILocalPlayer *localPlayer = GetLocalPlayer(); for (auto it = GetPlayerIterator(); it; ++it) { IPlayer *player = *it; if (!localPlayer->Equals(player) && player->IsValid()) { //mach was mit player //Abstand berechnen etc Vector delta = localPlayer->GetOrigin() - player->GetOrigin(); //hier benötige ich dann das IPlayer Interface von ILocalPlayer } delete player; } localPlayer->moveTo(...); delete localPlayer;Probleme bereitet jetzt die Equals Methode. Dadurch, dass jetzt die Basisklasse virtuell ist, verweigert der Compiler den cast. Bisher sah mein Equals so aus:
class IPlayer { public: virtual bool IsValid() = 0; virtual bool Equals(const IPlayer *other) = 0; }; class SourcePlayer : virtual public IPlayer { public: SourcePlayer(int index) : index(index) { } virtual bool IsValid() { return true; } virtual bool Equals(const Player *other) { const SourcePlayer *p = static_cast<const SourcePlayer*>(other); return index == p->index; } private: int index; };Jetzt beschwert sich der Compiler beim Cast und ein reinterpret_cast liefert natürlich nur noch Datenmüll.
Wie könnt ich das Problem lösen? Ob Code oder Designvorschlag ist mir relativ egal.greetz KN4CK3R
-
Du willst einen
SourcePlayermit einemSourcePlayervergleichen. Dann solltest Du das auch ausdrücken:bool Equals(const SourcePlayer *other) //nicht polymorph, da ohnehin invariant { return index == other->index; }
-
in dem Fall kann ich aber nicht mehr vom allgemeinen IPlayer, den ich in meiner Schleife habe, Equals aufrufen. Irgendwo muss ich ja hochcasten damit ich Zugriff auf index habe. Das hochcasten ist an der Stelle in Ordnung, da garantiert ist, dass immer nur eine Art von Player vorkommt.
Als ich das gestern in VS getestet habe, stand nach dem cast nur Blödsinn in index, das ist beim gcc offenbar anders, dafür bekomme ich eine free Fehlermeldung
(auch ohne Equals)greetz KN4CK3R
-
Du hast immer noch keinen virtuellen Dtor. Abgesehen davon finde ich deine Klassennamen schrecklich. Was ist denn bitte ein "LocalPlayer" oder ein "SourceLocalPlayer" und warum zur Hölle ein DoD.
-
Das ist natürlich nur ein Minimalbeispiel hier fürs Forum *Kopfschüttel*
Die Klassen heißen so weil es dabei um die SourceEngine geht. In meinem Projekt heißen die Klassen auch anders.
Wie man da den DoD vermeiden kann, war gerade meine Frage, falls du die bisherigen Posts nicht lesen wolltest...greetz KN4CK3R
-
KN4CK3R schrieb:
Wie man da den DoD vermeiden kann, war gerade meine Frage...
aber es wäre besser, einen neuen Blickwinkel zu finden

z.B:
struct IShooter { virtual bool CanShoot () const = 0; }; enum PlayerChar { LocalPlayerChar, SourcePlayerChar }; struct IPlayer { virtual bool isValid () const =0; virtual IShooter * getShooter () =0; }; ...
-
ich hab mir sowas ähnliches überlegt:
class IPlayer { public: virtual bool IsValid() = 0; virtual bool Equals(const IPlayer &player) = 0; }; class ILocalPlayer : public IPlayer { public: virtual bool CanShoot() = 0; }; class SourceLocalPlayer: public ILocalPlayer { public: SourceLocalPlayer(SourcePlayer &player) : player(player) { } virtual bool IsValid() { return player.IsValid(); } virtual bool CanShoot() { return false; } virtual bool Equals(const IPlayer &player) { return this->player.Equals(player); } private: SourcePlayer &player; };hier bekommt der LocalPlayer dann sein eigentliches Player Objekt im Konstruktor mit und ruft dann jeweils dessen Implementierung des Interfaces auf. Dadran stört mich aber etwas, dass die SourceLocalPlayer "is a" SourcePlayer Verbindung verloren geht und ich quasi alle Funktionsaufrufe weiterleite (solange sie gleich sind).
greetz KN4CK3R
-
class ILocalPlayer : public IPlayer
{
public:
virtual bool CanShoot() = 0;
};class SourceLocalPlayer: public ILocalPlayer
{
public:
SourceLocalPlayer(SourcePlayer &player)
: player(player)eigentlich sieht das nicht schön aus.. können Sie erzählen, welche Unterschiede es zwischen Player-Klassen gibt ? dann wird es leichter eine richtige Lösung zu finden..

-
wie gesagt gibt es die normalen Spieler, deren Hauptunterschied zum lokalen Spieler ihr Schreibschutz ist. Auf andere Spieler kann nur lesend zugegriffen werden. (GetName, GetHealth, GetOrigin, ...) Der lokale Spieler bietet die gleichen Methoden an (er ist ja auch einer der Spieler), bietet aber noch zusätzlich Methoden an, mit denen er verändert werden kann (SetAngles, Shoot, MoveForward, ...)
Also dachte ich eben an die von mir erstellen Interfaces
class IPlayer { public: GetName = 0; GetHealth = 0; GetOrigin = 0; ... }; class ILocalPlayer : public IPlayer //Vererbung weil ILocalPlayer ist ein IPlayer { public: SetAngles = 0; Shoot = 0; MoveForward = 0; ... }; class ConcretePlayer : public IPlayer //ist ein IPlayer { public: ...IPlayer Methoden... }; class ConcreteLocalPlayer : public ConcretePlayer, public ILocalPlayer //ist ein ConcretePlayer und ist ein ILocalPlayer => DoD { public: ...ILocalPlayer Methoden... //sollte von ConcretePlayer erben, damit hier nicht noch alle IPlayer Methoden implementiert werden müssen };greetz KN4CK3R
-
KN4CK3R schrieb:
Also dachte ich eben an die von mir erstellen Interfaces
dieser Vorgang nimmt viel Mühe umsonst..
KN4CK3R schrieb:
wie gesagt, gibt es die normalen Spieler, deren Hauptunterschied zum lokalen Spieler ihr Schreibschutz ist. Auf andere Spieler kann nur lesend zugegriffen werden.
es darf mithilfe von zwei Funktionen gelöst werden : getPlayerInfo () und getPlayerInfoConst.. und keine unnötige Vererbung..
-
du meinst ILocalPlayer sollte gar nicht von IPlayer erben, sondern nur mit einer Methode einen IPlayer zurückgeben?
greetz KN4CK3R
-
KN4CK3R schrieb:
du meinst ILocalPlayer sollte gar nicht von IPlayer erben, sondern nur mit einer Methode einen IPlayer zurückgeben?
es kann sein.. um diese Frage zu beantwotrten, ist es mehr Information nötig, was diese Klasse darstellen.
-
mehr Informationen kann ich nicht geben, da die "Lese Daten aus" Funktionalität die Hauptaufgabe der Spielerklassen sind.
greetz KN4CK3R
-
nicht diese Information, sondern : was für eine Konzeption haben Sie ausgewählt, die die Local-, Source-, und LocalSourcePlayers fordert.. Ich kann mir nicht vorstellen wozu braucht man so was..
-
wie ich das ganze benutze, habe ich hier beschrieben:
http://www.c-plusplus.net/forum/p2210487#2210487greetz KN4CK3R
-
KN4CK3R schrieb:
wie ich das ganze benutze, habe ich hier beschrieben:
http://www.c-plusplus.net/forum/p2210487#2210487ich hab das gelesen.. aber kann ich mir ein klares Bild nicht vorstellen..
die wichtigsten Momente liegen doch im Hintergrund..KN4CK3R schrieb:
Ich hab im Spiel mehrere Spieler, einer davon ist der lokale Spieler.
meinen Sie, ein Lokalplayer ist ein "Ich"-Player, also der Player, den ich steurn kann ? was ist dann LokalSourcePlayer ?!
-
genau, IPlayer sind alle Spieler und ILocalPlayer ist der eine Spieler, den ich selbst steuern kann. Aus diesem Grund ist er auch der einzige, den ich nicht nur auslesen kann. Source(Engine)LocalPlayer heißt meine Implementierung des ILocalPlayer-Interfaces für die Source Spieleengine.
greetz KN4CK3R
-
KN4CK3R schrieb:
genau, IPlayer sind alle Spieler und ILocalPlayer ist der eine Spieler, den ich selbst steuern kann.
greetz KN4CK3Rdann das vorgeschlagene Modell mit der Vererbung ist nicht richtig..
jetzt versuche ich es zu beschreiben..