C++ in Klassen auf eine Reihe von Objekten zugreifen (Schiffe versenken)

F

Furtano

Gast
Hi,

ich programmiere Schiffe-Versenken in C++ (später grafisch mit OpenGl).
Leider bin ich in der Objektorientierung neu und weiß nicht weiter.

Wie kann ich in der Klasse Shot überprüfen ob eines der erzeugten Schiffe der Klasse Ship getroffen wurde?

Ich hatte die Idee die Schiffe als Objekt-Stack an Shot zu übergeben aber er sagt man kann kein unbegrenztes Array übergeben.

Ich muss ja irgendwie SCHON auf die ganzen erzeugten Objekte zugreifen?

shot.c
PHP:
#include "shot.h"
#include "ship.h"

Shot::Shot(){

}

// Wurde ein Schiff getroffen?
// gehe alle Schiffe durch und überprüfe ob getroffen	
// Parameter Ship stack[] geht so nicht ?!?!?!	
void Shot::calcIsShipHit(int x, int y, Ship allShips[]){
	
}

ship.h
PHP:
#ifndef SHIP_H
#define SHIP_H

class Ship {

private:
	// POSITION BEGINNT AN DER LINKEN OBEREN ECKE 
	int positionX;
	int positionY;

	int canonPower;


	bool isEnemy;
	// 0 = Schiff liegt horizontal
	// 1 = Schiff liegt vertikal
	bool parking;

	// An welcher Stelle wurde das Schiff getroffen ?
	bool whereHit[];

public:
	// Konstruktor
	Ship(int length);
	void draw();
	void destroy();
	void shoot(int x, int y);


	void setPositionX(int x);
	int getPositionX();

	void setPositionY(int y);
	int getPositionY();

	void setIsEnemy(bool side);
	bool  getIsEnemy();

	void setParking (bool parking);
	bool getParking();

	void setWhereHit (int position);
	bool getWhereHit (int position);
};
 
Zuletzt bearbeitet von einem Moderator:
Hallo,
in der Zeile 10 bekommst du ja ein Array aller Schiffe. Dann kannst Du da ja drauf zugreifen und mit "int x, int y" und "positionX, positionY" schauen ob ein Schiff getroffen wurde.

Ob die Klasse "Shot" das aber machen sollte ist etwas fraglich, normalerweise würde man eher so eine Art "Collision-Handler" sprich eine eigene Klasse bauen, die dann auf treffer prüft.

statt
void Shot::calcIsShipHit(int x, int y, Ship allShips[]){

solltest du oben "#include <list>" machen und dann da eine Liste übergeben.

sprich:
void Shot::calcIsShipHit(int x, int y, list<Ship> allShips){

oder wenns doch mit Array sein soll
void Shot::calcIsShipHit(int x, int y, int shipCount, Ship* allShips){

Andy
 
Code:
void Shot::calcIsShipHit(int x, int y, Ship allShips[]){
}

Was genau geht daran nicht? Poste mal bitte die Fehlermeldung, die dir dein Compiler ausspuckt. Eigentlich ist der Code (rein syntaktisch) vollkommen korrekt (obwohl du noch einen zusätzlichen Parameter übergeben solltest, der angibt, wieviele Elemente dein übergebenes allShips-Array hat). Also

Code:
void Shot::calcIsShipHit(int x, int y, Ship allShips[], std::size_t numberOfElementsInArray){
}

P.S. Arbeitest du vielleicht mit Visual Studio? Wenn ja, bitte deine shot.c in shot.cpp umbenennen, sonst wird Visual Studio die Datei als C-Code und nicht als C++-Code übersetzen.

P.S. #2: Das hier ...

Code:
class Ship {
    // An welcher Stelle wurde das Schiff getroffen ?
    bool whereHit[];
};

geht natürlich nicht. Wenn du in C++ ein Array als Member einer Klasse deklarierst, mußt du schon angeben, wie groß das Array sein soll. Also z.B.

Code:
class Ship {
    // An welcher Stelle wurde das Schiff getroffen ?
    static const std::size_t ARRAY_SIZE = 20;

    bool whereHit[ ARRAY_SIZE ];
};
 
Zuletzt bearbeitet:
Ok ich machs als Liste, is vielleicht einfacher.

Allerdings sagt Visual Studio 2010 mir: list is not a template (shot.cpp)



shot.cpp
PHP:
#include "shot.h"
#include "ship.h"
#include <list>

Shot::Shot(){

}

// Wurde ein Schiff getroffen?
// gehe alle Schiffe durch und überprüfe ob getroffen		
void Shot::calcIsShipHit(int x, int y, list<Ship> allShips){

	
}

shot.h
PHP:
#ifndef SHOT_H
#define SHOT_H

class Shot{

private: 

public:
	Shot();
	void calcIsShipHit(int x, int y, list<Ship> allShips);

}



#endif


main.cpp

PHP:
...
#include <list>
 
Muß std::list heißen (weil es sich im namespace std befindet). Wenn du den weg über std::list wählst (ich würde std::vector empfehlen - ist effizienter), solltest du aber bedenken, daß du - wenn du die in der list / vector enthaltenen Ship-Objekte aus der Methode calcIsShipHit() heraus ändern möchtest - die list bzw. den vector per Referenz übergeben mußt.

Code:
void calcIsShipHit(int x, int y, std::list<Ship>[B][COLOR="Red"]&[/COLOR][/B] allShips);

// oder:
void calcIsShipHit(int x, int y, std::vector<Ship>[B][COLOR="Red"]&[/COLOR][/B] allShips);

Ansonsten würdest du der calcIsShipHit()-Methode eine KOPIE deiner Original-list/vector übergeben, und alle innerhalb der Methode vorgenommenen Änderungen würden nur diese Kopie betreffen, die Original-list/vector aber unverändert lassen.

P.S. Du mußt <list> in shot.h includen.
 
Zuletzt bearbeitet:
ok hab ich gemacht (vector) jetzt sagt er mir in der shot.cpp

errorfwdjw.png
 
Hast du statt <list> nun auch <vector> inkludiert (und zwar in shot.h)?
 
"Hast du statt <list> nun auch <vector> inkludiert (und zwar in shot.h)? "
ja habe ich, in der Header wird kein Fehler angezeigt, nur in der shot.cpp


shot.h
PHP:
#ifndef SHOT_H
#define SHOT_H
#include <vector>

class Shot{

private: 

public:
	Shot();
	void calcIsShipHit(int x, int y, std::vector<Ship> &allShips);

}



#endif
 
Du mußt natürlich auch den Header, in dem die Klasse Ship definiert ist, in shot.h inkludieren (oder zumindest eine Vorwärtsdeklaration liefern).
 
Jetzt frage ich mich allerdings, wozu es die Klasse Shot überhaupt gibt. Sie hat keine Datenmember und keine virtuellen Methoden. Du könntest calcIsShipHit() doch eigentlich genau so gut als freie Funktion definieren, oder?
 
Ja das stimmt, aber ich will ja objektorientiert proggen :)
Ergänzung ()

ok nächstes Problem:
Die Membervariable int whereHitStatus[givenLength] von der Klasse Ship soll die Größe durch den im Parametrisierten Konstruktor gegebenen Length deklariert werden.

Also noch mal einfach:

Compiler sagt -> (whereHitStatus) Expression must have a constant value

Ship.h
PHP:
private:
    int length;
    bool whereHitStatus[length];

public: 
    Ship(int givenLength);

ship.c
PHP:
Ship::Ship(int givenLength){
	this->length = givenLength;
}

Wie komme ich weiter ?
// edit, Oh ich könnte ja wieder nen vector dafür nehmen, oder ?
Aber müsste theoretisch doch auch mit C-Style gehen
 
Zuletzt bearbeitet von einem Moderator:
Code:
bool whereHitStatus[length];

length muss ein konstanter Wert sein (also einer der sich nicht ändert). Dies kannst du nur erreichen indem du eine Zahl eingibst oder eine Variable per "#define" oder "const int" global machst (am besten in der Headerdatei).

So wie ich das aber sehe, willst du in "whereHitStatus[length]" true setzten sobald man dort reinschießt richtig? Du weisst aber das ein Spielfeld 10x10 Felder hat, ja? Dann könntest du ein zweidimensionales Array machen. Das sieht dann so aus:

Code:
bool whereHitStatus[10][10];
 
Konstante und Initialisierung im Konstruktor müsste tun.
Code:
private:
    const int length;
    bool whereHitStatus[length];

public: 
    Ship(int givenLength);

ship.c
Code:
Ship::Ship(int givenLength): length(givenLength){}
 
Dann nenn die Klasse Shot halt "ShotUtil" oder "CollisionUtil" und mach die Methode statisch... oder bau die Methode dort ein, wo die Liste der Schiffe existiert...
 
nullPtr schrieb:
Konstante und Initialisierung im Konstruktor müsste tun.
Code:
private:
    const int length;
    bool whereHitStatus[length];

public: 
    Ship(int givenLength);

ship.c
Code:
Ship::Ship(int givenLength): length(givenLength){}

Da diese length-Konstante für alle Instanzen der Klasse die selbe ist, kann man sie auch gleich static machen (hatte ich ja oben schon mal gezeigt). Außerdem würde ich für Größenangaben von Arrays std::size_t den Vorzug gegenüber int geben.
Ergänzung ()

Furtano schrieb:
Ja das stimmt, aber ich will ja objektorientiert proggen :)

Furtano, objektorientierte Programmierung heißt nicht, daß man auf Teufel komm raus alles mit Klassen und Objekten macht. Setze da Klassen ein, wo es Sinn macht, und NUR da. Eine Klasse ohne virtuelle Methoden und ohne Datenmember ist in den meisten Fällen absolut sinnlos.

Nur falls es dich interessiert, deine Methode void Shot::calcIsShipHit(int x, int y, std::vector<Ship> &allShips) verwendet keinerlei nicht-statische Member der Klasse Shot. Das heißt, die Methode braucht eigentlich auch keine Instanz der Klasse Shot. Fakt ist aber, daß jedes mal, wenn die Methode aufgerufen wird, auch eine Instanz der Klasse Shot übergeben wird. Mach dir bewußt, daß nichtstatische Methoden immer einen sogenannten this-Pointer haben. Der fällt nicht einfach so vom Himmel sondern wird implizit als zusätzlicher Parameter übergeben. Soll heißen, unter der Haube sieht deine calcIsShipHit-Methode eigentlich so aus.

Code:
void calcIsShipHit( [B][COLOR="Red"]Shot* this[/COLOR][/B], int x, int y, std::vector<Ship> &allShips)

Da du diesen this-Pointer aber in der Methode gar nicht brauchst, wird der völlig umsonst übergeben. Ist doch unnötig ineffizient, oder? Ich würde zwar niemals zu Optimierungen raten, bis sich zeigt, daß sie nötig sind, aber man muß ja auch nicht von Anfang an gleich Pessimierungen einbauen, oder? ;)
Also mach die Methode zu einer freien Funktion oder mach sie wenigstens static, wie 1668mib schon vorgeschlagen hat.
Ergänzung ()

nullPtr schrieb:
Konstante und Initialisierung im Konstruktor müsste tun.
Code:
private:
    const int length;
    bool whereHitStatus[length];

public: 
    Ship(int givenLength);

ship.c
Code:
Ship::Ship(int givenLength): length(givenLength){}


Das wird in C++ übrigens nicht funktionieren. In C++ __muß__ die Größe eines Arrays zur Kompilierzeit bereits feststehen. Mit deinem Code stünde diese Größe aber erst zum Zeitpunkt der Instanziierung eines Objektes der Klasse fest.

So würde es gehen:

Code:
class Ship {
 
private: 
	// An welcher Stelle wurde das Schiff getroffen ?
	std::vector< bool > whereHit;
	
public:
	Ship( std::size_t theSize ) : whereHit( theSize ) {}
};
 
Zuletzt bearbeitet:

Ähnliche Themen

Zurück
Oben