Problem mit this und Konvertierungsoperator?



  • Hallo, ich habe eine Headerdatei geschreiben, die Zeilenweise aus einer Datei lesen soll, schauen soll, was in der Zeile steht und entsprechend eine Struktur erzeugen:

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <ostream> 
    
    using namespace std;
    
    namespace all_cards_aviable
    {
    	class Kreatur;
    	class Zombie;//Zeile 30
    	class Moorleiche;
    
    	class Karte
    	{
    	public:
    		inline Karte(){}
    		virtual ~Karte() = 0;
    	};
    
    	class Player
    	{
    	public:
    		Karte Deck[60];
    		Karte Friedhof[60];
    		Karte ingame[60];
    		int Kreaturen;
    	         int HP, AP;
    
    		bool fill_deck(char* nDeck);//Zeile 50
    		bool KarteSpielen(Karte* Card);
    		Player(char* nDeck);
    		~Player();
    	};
    	bool Player::fill_deck(char* nDeck)
    	{
    		ifstream in;
    		Player* that = this;
    		for(string s; getline(in, s);) 
    		{
    		if(s == "Zombie")
    		{
    			for (int i=0; i<60;i++)
    			{
    				if(Deck[i])
    				{}
    				else
    				{
    					Deck[i] = /*new*/Zombie(*that);
    					break;
    				}
    			}
    			break;
    		}
    		if(s == "Moorleiche")
    		{
    			for (int i=0; i<60;i++)
    			{
    				if(Deck[i])
    				{}
    				else//Zeile 80
    				{
    					Deck[i] = /*new*/Moorleiche(*that);
    					break;
    				}
    			}
    			break;	
    		}
    		}
    		return(true);
    	}
    
    	Player::Player(char* nDeck)
    	{
    		if(fill_deck(nDeck))
    		{
    			HP = 2000;
    			AP = 10;
    			Kreaturen = 0;
    		}
    		else
    		{
    		}
    	}
    
    	Player::~Player()
    	{
    		for(int i=0; i<60; i++)
    		{
    			//delete Deck[i];
    		}
    	}
    
    	class Kreatur : public Karte
    	{
    	private:
    		Player* owner;
    		string typ;
    		int Angriff, Verteidigung, Lebenspunkte, maxAngriff, maxVerteidigung, maxLebenspunkte;
    	public:
    		int* reAngriff()
    		{
    			return(&Angriff);
    		}
    
    		int* reVerteidigung()
    		{
    			return(&Verteidigung);
    		}
    
    		int* reLebenspunkte()
    		{
    			return(&Lebenspunkte);
    		}
    		inline Kreatur(){}
    		virtual ~Kreatur() = 0;
    		bool Angreifen(Kreatur* opponent)
    		{
    			*opponent->reAngriff() -= (Angriff - opponent->Verteidigung)
    		}
    	};
    
    	class Zombie : public Kreatur
    	{
    	public:
    		Zombie(Player* nowner)
    		{
    			owner = nowner;
    			typ = "Untoter";
    			nowner->Kreaturen++;
    			Angriff = maxAngriff = 1000;
    			Verteidigung = maxVerteidigung = 600;
    			Lebenspunkte = maxLebenspunkte = 600;
    		}
    	};
    
    	class Moorleiche : public Kreatur
    	{
    	public:
    		Moorleiche(Player* nowner)
    		{
    			owner = nowner;
    			typ = "Untoter";
    			nowner->Kreaturen++;
    			Angriff = maxAngriff = 1800;
    			Verteidigung = maxVerteidigung = 1000;
    			Lebenspunkte = maxLebenspunkte = 1400;
    		}
    	};
    }
    

    Dann habe ich folgende Testdatei geschrieben:

    #include "C:/Programme/Microsoft Visual Studio/MyProjects/Jugiohspiel/Enum.h"
    using namespace all_cards_aviable;
    
    void main()
    {
    	Player Ansgar("C:/Programme/Spiel/Decks/Deckundead.txt");
    }
    

    In der Datei Deckundead.txt steht nur 4 mal Zombie untereinander

    beim kompilieren erhalte ich folgende Fehler:

    error C2451: Bedingter Ausdruck des Typs 'class all_cards_aviable::Karte' nicht zulaessig
            Kein benutzerdefinierter Konvertierungsoperator verfuegbar, der diese Konvertierung durchfuehren kann, oder der Operator kann nicht aufgerufen werden
    
    error C2440: 'type cast' : 'class all_cards_aviable::Player' kann nicht in 'class all_cards_aviable::Zombie' konvertiert werden
            Quelle oder Ziel hat einen unvollstaendigen Typ
    
    error C2451: Bedingter Ausdruck des Typs 'class all_cards_aviable::Karte' nicht zulaessig
            Kein benutzerdefinierter Konvertierungsoperator verfuegbar, der diese Konvertierung durchfuehren kann, oder der Operator kann nicht aufgerufen werden
    
    error C2440: 'type cast' : 'class all_cards_aviable::Player' kann nicht in 'class all_cards_aviable::Moorleiche' konvertiert werden
            Quelle oder Ziel hat einen unvollstaendigen Typ
    

    Kann mir da jemand helfen?

    thx
    Glamdring



  • oh, MTG? 🙂

    Das Problem ist, 'Karte' ist abstrakt, da kannst du nicht mit Karte deck[60]; ein Array davon anlegen. Was du hier brauchst ist ein Array von Zeigern auf Karten. Karte* deck[60];



  • allerdings MTG 🙂

    Dadurch gehen die Fehler bezüglich der if - Abfrage weg, er denkt aber weiterhin das ich mit this den Zombie meine, nicht den Spieler.

    thx
    Glamdring



  • deck[i] = Zombie() geht natürlich nicht. Nun ist Deck ja ein Array von Zeigern auf die Basisklasse. deck[i] = new Zombie(); müsste das heißen.

    Ausserdem ist Zombie zu dem Zeitpunkt garnicht vollstandig bekannt, weshalb das auch nicht gehen wird.



  • Ja, das hab ich auch festgestellt und darum Karte, Kreatur, Zombie und Moorleiche in eine andere headerdatei ausgelagert, jetzt gibts allerdings Probleme mit dem undefinierten Player, und der Sturheit meines Compilers (VC++)
    Cards.h

    #ifndef CARDS
    #define CARDS
    #include <string.h>
    #include "Enum.h"
    namespace all_cards_aviable
    {
    	class Player;
    	class Karte;
    	class Kreatur;
    	class Zombie;
    	class Moorleiche;
    	enum type{Untoter, Geist};
    }
    
    class all_cards_aviable::Karte
    	{
    	public:
    		inline Karte(){}
    		virtual ~Karte() = 0;
    	};
    
    class all_cards_aviable::Kreatur : public Karte
    	{
    	public:
    		Player* owner;
    		enum type typ;
    		int Angriff, Verteidigung, Lebenspunkte, maxAngriff, maxVerteidigung, maxLebenspunkte;
    	public:
    
    		int* reAngriff()
    		{
    			return(&Angriff);
    		}
    
    		int* reVerteidigung()
    		{
    			return(&Verteidigung);
    		}
    
    		int* reLebenspunkte()
    		{
    			return(&Lebenspunkte);
    		}
    		inline Kreatur(){}
    		virtual ~Kreatur() = 0;
    		bool Angreifen(Kreatur* opponent)
    		{
    			*opponent->reAngriff() -= (Angriff - opponent->Verteidigung);
    		}
    	};
    
    class all_cards_aviable::Zombie : public Kreatur
    	{
    	public:
    		Zombie(Player* nowner)
    		{
    			owner = nowner;
    			typ = Untoter;
    			nowner->Kreaturen++;
    			Angriff = maxAngriff = 1000;
    			Verteidigung = maxVerteidigung = 600;
    			Lebenspunkte = maxLebenspunkte = 600;
    		}
    		~Zombie()
    		{
    			delete this;
    		}
    	};
    
    	class all_cards_aviable::Moorleiche : public Kreatur
    	{
    	public:
    		Moorleiche(Player* nowner)
    		{
    			owner = nowner;
    			typ = Untoter;
    			nowner->Kreaturen++;
    			Angriff = maxAngriff = 1800;
    			Verteidigung = maxVerteidigung = 1000;
    			Lebenspunkte = maxLebenspunkte = 1400;
    		}
    		~Moorleiche()
    		{
    			delete this;
    		}
    	};
    #endif
    

    Enum.h

    #ifndef ENUM
    #define ENUM
    #include <iostream>
    #include <fstream>
    #include <string>
    #include <ostream> 
    #include "cards.h"
    
    using namespace std;
    
    namespace all_cards_aviable
    {	
    	class Player
    	{
    	public:
    		Karte* Deck[60];
    		Karte* Friedhof[60];
    		Karte* ingame[60];
    		int Kreaturen;
    		//Ort Place;
    		int HP, AP;
    
    		bool fill_deck(const char* nDeck);
    		bool KarteSpielen(Karte* Card);
    		Player(const char* nDeck);
    		~Player();
    	};
    	bool Player::fill_deck(const char* nDeck)
    	{
    		ifstream in;
    		for(string s; getline(in, s);) 
    		{
    		if(s == "Zombie")
    		{
    			for (int i=0; i<60;i++)
    			{
    				if(Deck[i])
    				{}
    				else
    				{//Zeile 40
    					Deck[i] = new Zombie(this);
    					break;
    				}
    			}
    			break;
    		}
    		if(s == "Moorleiche")
    		{
    			for (int i=0; i<60;i++)
    			{
    				if(Deck[i])
    				{}
    				else
    				{
    					Deck[i] = new Moorleiche(this);
    					break;
    				}
    			}
    			break;	
    		}//Zeile 60
    		}
    		return(true);
    	}
    
    	Player::Player(const char* nDeck)
    	{
    		if(fill_deck(nDeck))
    		{
    			HP = 2000;
    			AP = 10;
    			Kreaturen = 0;
    		}
    		else
    		{
    		}
    	}
    
    	Player::~Player()
    	{
    		for(int i=0; i<60; i++)//Zeile 80
    		{
    			delete[] Deck[i];
    		}
    	}
    }
    #endif
    

    und Test.cpp

    #include "C:/Programme/Microsoft Visual Studio/MyProjects/Jugiohspiel/Enum.h"
    using namespace all_cards_aviable;
    
    void main()
    {
    	Player* Ansgar;
    	std::string str = "C:/Programme/Jugiohspiel/Decks/Deckundead.txt";
    	Ansgar = &Player(str.c_str());
    	delete Ansgar;
    }
    

    dies sind die Fehler:

    error C2027: Verwendung des undefinierten Typs "Player"
    error C2227: Der linke Teil von '->Kreaturen' muss auf Klasse/Struktur/Union zeigen
    

    Der erste Fehler ist ja noch normal, der zweite ist aber etwas merkwürdig

    ???

    thx
    Glamdring



  • *push* 😃 🙂 😞 😡 😃



  • Glamdring schrieb:

    error C2027: Verwendung des undefinierten Typs "Player"

    Schau dir doch an, an welcher Stelle dieser Fehler gemeldet wird. Da verwendest du Player, indem du auf Player::Kreaturen zugreifst. Zu dem Zeitpunkt hat der Compiler aber nur eine Forward Declaration von Player, die dafür nicht ausreicht.

    Der zweite Fehler ist ein Folgefehler, den kannst du ignorieren.



  • Das war mir durchaus bewusst, mein Problem ist bloß, dass das niemals gehen kann, weil es eine hübsche kleine Dreiecksbeziehung ist:
    Player braucht Zombie -> Zombie vor Player
    Zombie braucht Karte -> Karte vor Zombie
    Karte braucht Player -> Player vore Karte //geht nicht

    weis da jemand Rat???

    thx
    Glamdring



  • Kein Problem: Die Klassendefinitionen dürfen Referenzen und Zeiger enthalten, wenn du eine Vorrausdeklaration darüber schreibst - und die sollten in jedem Fall reichen. Diejenigen Elementfunktionen, die mehr als eine Klassendeklaration benötigen, schreibst du dann entweder erst nach der Definition aller relevanten Klassen in die Header-Datei:

    inline void KlasseA::funktion()
    {
        /* Code, der KlasseB verwendet */
    }
    

    Oder du lagerst diese Funktionen gleich in eine .cpp-Datei aus (dann ohne das "inline"), was in größeren Projekten sowieso Vorteile bietet (übersichtlichere Header-Dateien, kürzere Kompilierzeiten).

    Ich will mich eigentlich nicht in dein Projekt einmischen, aber der Code hat davon abgesehen viele Stellen, die garantiert in einer Speicherverletzung enden und bevor du nur mit viel Frust und Nachfragen zu einem ausführbaren Ergebnis kommst, solltest du vielleicht erstmal mit einer einzelnen, möglichst vielseitigen Klasse herumexperimentieren, bevor du eine ganze Klassenhierarchie aus dem Nichts aufbaust.



  • Das mit dem Speicher ist ohnehin ein Problem von mir, da konnte mir bisher noch kein Buch helfen 😞 😞 😞 😡

    Aber ich muss erstmal etwas haben, womit ich herumexperimentieren kann, keine dieser Klassen hätte ohne eine andere einen Sinn (oder???)

    Gruß
    Glamdring



  • Hast du nicht mal geschrieben, dass du von Delphi her kommst? Vielleicht denkst du deshalb zu kompliziert und zeigerorientiert (ging mir am Anfang jedenfalls so...).
    Wenn du das Spiel nicht in kleinere Teilprojekte aufteilen kannst, mach einfach erstmal Pause damit und schreib eine Klasse für Zufallswürfel plus billiges Testprogramm o.ä. - wenn das Spiel hinten und vorne nicht kompiliert und noch an zig Stellen gleichzeitig Fehler enthalten kann, kannst du erst recht nicht experimentieren 🙂

    (Mir ist gerade erst aufgefallen, dass du das Speichermanagement vom ersten geposteten Quellcode zum zweiten ja verschlimmbessert hast. Im ersten sind die unnötigen/falschen Konstruktoren z.B. noch nicht - hatte ich vorhin gar nicht gesehen.)



  • Wat Kompliziert.

    Was passiert eigentlich wenn:

    class Zombie
    {
    public:
     virtual ~Zombie(){delete this}
    }
    
    void main()
    {
     Zombie* pZombie=new Zombie;
    
     delete pZombie; //Wer löscht jetzt wenn? Habe ich noch nie ausprobiert
    }
    

    Das durchgreiffen Onwer Klassen kann man elegant über abstrakte Klassen lösen

    class PlayerBasis
    {
    public:
     void virtual set()=0;
     int virtual get()=0;
    };
    
    class Kreature
    {
    public:
     Kreatur(PlayerBasis* p){m_pPlayerBasis=p;}
    };
    
    class Player:public PlayerBasis
    {
    public:
     void virtual set(){}
     int virtual get(){return 1;}
    };
    

    Empfehlung nicht direkt auf Membervariablen zugreifen. Am anfang scheint das unsinnig ist später aber sehr sehr wertvoll.



  • Nachtrag zum initailisieren

    void foo()
    {
    Player player;
    Kreature kreature(&player);
    }
    


  • 1. Kann mir nicht vorstellen von Delphi zu kommen, ich kann nicht mal Pascal 😃

    2. Speichermanagement ist eh nicht mein Ding siehe weiter oben

    3. Das mit dem delete , delete stimmt

    4. Was das zweite bedeutetd versteht ich net 🙄



  • idefix schrieb:

    delete pZombie; //Wer löscht jetzt wenn? Habe ich noch nie ausprobiert
    

    Netter Weise wird niemand gelöscht, weil delete pZombie vor der Speicherfreigabe erst den Destruktor von Zombie aufruft, der über delete this den Destruktor von Zombie aufruft, der über delete this... ... ... Der Speicher wird dann erst freigegeben, nachdem das Programm auf irgendeine Weise wegen des Stack-Overflows beendet wird 🙂



  • Du hast doch die Problematik der Dreiecksbeziehung beschreiben. Wenn du eine Basisklasse von Player deklarierst, können die Klassen Karte, Zombie, Moorleiche über die Basisklassen Definition auf das eigentliche Player Objekt zugreiffen. Die Überlegung ist das die Klassendefinition bekannt ist, aber das Objekt ein anders. Unterscheidung zwischen Klasse und Objekt ist wichtig.

    Ich bezweifel das dieses Problem mit der Aufteilung in .cpp/.h erfolgreich sein wird. Da die Include-Reihenfolge das gleiche/ähnliche Problem haben würde. Forward Deklaration der Klasse Player finde ich eigentlich nicht so besonders gut. Ist wohl eher eine Geschmackssache.

    Was ist wenn es unterschiedliche Player Klassen gibt? Okay, das war nicht die Frage.

    Du musst das auch nicht mit einer abstakten Klasse machen. Eine gewöhliche Klasse ist völlig ausreichend.



  • idefix schrieb:

    Ich bezweifel das dieses Problem mit der Aufteilung in .cpp/.h erfolgreich sein wird. Da die Include-Reihenfolge das gleiche/ähnliche Problem haben würde. Forward Deklaration der Klasse Player finde ich eigentlich nicht so besonders gut. Ist wohl eher eine Geschmackssache.

    Es ging mir nicht um die Aufteilung .h/.cpp, sondern um die Verwendung einer Vorwärtsdeklaration allgemein (von mir aus auch komplett im Header, wobei die Implementierungen weiter unten stehen).
    Warum sind Vorwärtsdeklarationen "nicht so besonders gut"? Ich sehe nicht einmal einen Nachteil, den man je nach Geschmack verschieden stark werten könnte. Die Dreiecksbeziehung über Vorwärtsdeklarationen (mit welcher Dateiorganisation auch immer) aufzulösen ist IMHO viel direkter als eine Basisklasse hinzuzüfügen, die keine Abstraktion bietet, sondern nur der Vermeidung dieser Vorwärtsdeklaration dient (und dabei vernachlässigbaren tatsächlichen und einige Tipparbeit mit viel geistigem Overhead einführt).



  • So, so keine Geschmakssache!?

    1. Ich habe nicht gesagt das Vorwärtsdeklarationen nicht besonders gut sind, sondern nur das ich (persönlich) diese nicht besonders gut finde.

    2. Ich habe auch nicht von irgendwelchen Nachteilen geredet.

    3. Geistiger Overhead? Schick formuliert! Warum so aggressive? Das Denken ist frei und jeder hat seine Vorlieben.

    4. Das eine Vrowärtsdeklaration direkter ist? Warum? Man könnte genauso gut sagen, das eine Basisklasse allgemeiner ist.

    Ich möchte mich mit dir nicht streiten, was nun besser oder schlechter ist. Ich denke das jedes für sich seine Vor- und Nachteile bietet.



  • idefix schrieb:

    1. Ich habe nicht gesagt das Vorwärtsdeklarationen nicht besonders gut sind, sondern nur das ich (persönlich) diese nicht besonders gut finde.

    2. Ich habe auch nicht von irgendwelchen Nachteilen geredet.

    Ich habe mich nur ziemlich gewundert, wo bei einer Vorwärtsdeklaration der Haken ist, der dich stört. Wenn du schlicht ein schlechtes Gefühl dabei hast, na von mir aus :), denn...

    3. Geistiger Overhead? Schick formuliert! Warum so aggressive? Das Denken ist frei und jeder hat seine Vorlieben.

    ...so aggressiv klingen wollte ich nicht, sorry wenn ich das getan habe 🙂

    4. Das eine Vrowärtsdeklaration direkter ist? Warum? Man könnte genauso gut sagen, das eine Basisklasse allgemeiner ist.

    Ich fand die Deklaration direkter, weil der Code, den er mit einer Vorwärtsdeklaration schreiben würde, exakt derselbe wäre (vom Text und der sprachtechnischen Bedeutung her), den er schreiben würde, wenn sein Beispiel auch ohne ginge - nur, dass er ihn mit der Vorwärtsdeklaration an einer anderen Stelle schreiben müsste.
    Und dass eine Basisklasse allgemeiner ist, will ich gar nicht bezweifeln 🙂



  • Ich habe mich einmal nach alternativen zu Pointern umgeschautr und Smart Poiunter gefunden. Dann habe ich mir #Boost gezogen, kann damit aber noch nichts anfangen, weil die Dokumentation absolut unzureichend ist, keine Schnittstellen usw., kann mir da jemand helfen???


Anmelden zum Antworten