Dynamische Objekte und Attribute

Neue Frage »

Auf diesen Beitrag antworten »
Tommy Dynamische Objekte und Attribute

Meine Frage:
Wir haben in den vergangenen Laboraufgaben einen Saugroboter modelliert.
Er hat die relevanten Attribute:

Ausrichtung m_Alpha
Durchmesser m_Diameter=0.4
X Position m_X
Y Position m_Y
gefahrene Strecke m_M

Aus der Ausrichtung und der Strecke konnten die Positionen X und Y errechnet werden. Dazu haben wir die Methoden setAlignment und setMovement entwickelt mit denen wir die Ausrichtung und die zurückgelegte Strecke eingeben konnten.
Mit der Methode print wurde dann die Position (X/Y) sowie die Ausrichtung Alpha angezeigt.

Nun sollen wir den Saugroboter visualisieren.
Dazu haben wir eine graphische Java Konsole bekommen, die parallel zu Eclipse im hintergrund läuft. Um diese Konsole ansprechen zu können sollten wir eine Klasse "World.h" in unsere Header Datei inkludieren.
Mit der Methode "show(m_X, m_Y, m_Alpha, m_Diameter)" veranlassen wir die Konsole einen Saugroboter an der angegebenen Stelle mit der angegebenen Ausrichtung anzuzeigen.

Und jetzt fangen meine Probleme an, ich zitiere:

"Ergänzen Sie nun in der Implementierung ihrer Saugroboterklasse ein Attribut "World* m_world" mit dem Initialwert NULL. Ergänzen Sie weiter die Methode "void setWorld(World* world)", die dem Attribut den übergebenen Wert zuweist."


Meine Ideen:
Das Asterix bedeutet doch, dass das ein Pointer ist? Also ein Pointer den ich World nenne und der auf m_World zeigt?
Die Werte die ich dem Attribut übergeben will sind doch mein Ausrichtungswinkel und die Strecke, woraus dann noch x und y berechnet werden.
Das müsste ich dann eventuell vorher berechnen und dann die Ergebnisse an m_World übergeben?
Das sind doch mehrere Werte dh m_world müsste dann ein array sein?
Im Grunde will ich die Parameter meiner Saugroboter-Klasse an die World-Klasse übergeben, damit die Konsole diese dann grafisch darstellen kann?
Dafür soll ich ja die Methode "setWorld" schreiben ... wird das dann ein copy Konstruktor? Aber der kann doch nur Werte derselben Klasse kopieren ?

Ich weiss nicht wie dieses Attribut "World* m_world" zu verstehen ist, deswegen hab ich auch eine Ahnung wie ich denn jetzt die Werte daran übergeben kann.
 
Auf diesen Beitrag antworten »
eulerscheZahl

Ja, es wird ein Pointer auf eine Klasse World übergeben.
Wie du ein Objekt erzeugst, entnimmst du am besten der Headerdatei: wird einen Blick hinein und suche einen Konstruktor. Da siehst du dann, was für Argumente der erwartet.

edit: ich glaube, ich habe es gefunden:
Zitat:
Constructor Summary
World(int worldWidth, int worldHeight, int cellSize)
Construct a new world.
World(int worldWidth, int worldHeight, int cellSize, boolean bounded)
Construct a new world.

(von http://www.greenfoot.org/files/javadoc/greenfoot/World.html)
Auf diesen Beitrag antworten »
Tommy

Vielen Dank für Deine Antwort :-)

Wir sollen erst ein Objekt der Klasse World erzeugen (und es dann verwirrenderweise world nennen) und dann ein Objekt der Klasse CVacubot (So habe ich meine Saugroboterklasse gennannt). Und dann sollen wir das Objekt world der Saugroboterklasse mit der Methode setWorld bekannt machen.
In der main sähe das dann so aus:

int main void()
{
World world;
CVacubot Saugroboter;

Saugroboter.setWorld();

return 0;
}

Jetzt ist es aber so, dass wir ja die Position des Saugroboters mit den vorher geschriebenen Methoden setAlignment und setMovement bewegen können. Also ändern sich die Parameter nicht nur für die Klasse CVacubot, sondern dann ja auch für die Klasse World.

Das bedeutet doch, dass mein Objekt world dynamisch sein muss?

Ich werde erstmal Deinem Hinweis nachgehen und mich mit der Übergabe per Pointer beschäftigen, vielen Dank dafür :-)
Auf diesen Beitrag antworten »
Tommy

Der Absatz im Skript über die Übergabe per Pointer hat mir nicht geholfen.
Letztendlich steht da, dass eine Übergabe per reference grundsätzlich vorzuziehen ist, da sie das gleiche macht, aber weniger fehleranfällig ist.

Mir ist einfach nicht klar, wie das Attribut "World* m_World" zu verstehen ist.
Ich habe das in meiner CVacubot.h so implementiert:

Zitat:

#define CVACUBOT_H_

#include "cleanerbot/World.h"
class CVacubot {

private:
double m_Diameter;
double m_X;
double m_Y;
double m_Alpha;
int m_M;
World* m_World;

public:
CVacubot();
~CVacubot();
double getPosition();
double getAlignment();
double setAlignment(double Alpha);
int setMovement(int M);
void print();
void setWorld(World* world);

};


Im .cpp file habe ich dann im Default Konstruktor "m_World=0" gesetzt um es wie in der Aufgabenstellung gefordert mit NULL zu initialisieren.

Bisher sieht meine Methode im .cpp file so aus:

Zitat:

#include <iostream>
#include <stdlib.h>
#define _USE_MATH_DEFINES
#include <cmath>
using namespace std;
#include "CVacubot.h"
#include "cleanerbot/World.h"

CVacubot::CVacubot()
{
m_Diameter=0.4;
m_X=0;
m_Y=0;
m_Alpha=0;
m_M=0;
m_World=0;
}

void CVacubot::setWorld(World* world)
{


m_World->show(m_X, m_Y, m_Alpha, m_Diameter=0.4);

return;
}


Sobald ich die Methode der Klasse World "show(...)" aufrufe stürtzt auch die .exe von meinem Programm ab :-(
 
Auf diesen Beitrag antworten »
eulerscheZahl

In setWorld wird doch nichts angezeigt, da wird nur m_World zugewiesen, sodass dort kein nullpointer mehr steht. Du willst eine Methode eines nicht existierenden Objekts aufrufen.
Die Methode show sollst du vermutlich selbst implementieren, zumindest ist sie nicht Teil von World (siehe oben verlinkte Doku).
Auf diesen Beitrag antworten »
Tommy

Die World.h die wir verwenden ist eine ganz andere, als das was Du da oben verlinkt hast.
Die show Methode ist dort auch gegeben:

Zitat:

void World::show
219 (double positionX, double positionY, double angle, double diameter) {
220 stringstream cmd;
221 cmd << "VACUUM CLEANER "
222 << positionX << ","
223 << positionY << ";"
224 << cos(angle) << "," << sin(angle) << ";"
225 << diameter;
226 sendCommand(cmd);
227 }


Und wir sollen diese Methode in "setWorld" aufrufen, sowie in jeder anderen Methode die die Position des Roboters ändert.

Ich bin jetzt soweit, dass ich denke, dass das Attribut "World* m_World" ein Attribut ist, dass "m_World" heisst und zur Klasse "World" gehört.

Was ich immer noch nicht verstehe ist, wie ich dem Attribut m_World die Werte für m_X, m_Y, m_Alpha und m_Diameter übergebe.

Ausserdem verstehe ich nicht wieso in der Methode "setWorld(World* world)" steht und nicht "setWorld(World* m_World)".
Auf diesen Beitrag antworten »
eulerscheZahl

Dann lass uns nicht weiter Rätselraten spielen. Gib' mir deine Informationen (wie z.B. den besagten Header), dann kann ich auch verstehen, worum genau es geht.

m_World und world sind beiden Instanzen der Klasse World.

Sehe ich das richtig, das besagte Anzeige mittels Kommandozeile aufgerufen wird?
Auf diesen Beitrag antworten »
Tommy

Entschuldige bitte, ich dachte ich hätte alle notwendigen Informationen geliefert.

Erstmal die Aufgabe:

Zitat:

Um die Bewegung des Saugroboters veranschaulichen zu können, steht Ihnen eine graphische Konsole zur Verfügung. Natürlich müssen Sie dieser konsole für eine korrekte Anzeige die aktuelle Position des Saugroboters "bekanntgeben". Das soll in dieser Aufgabe implementiert werden.

a) Die Konsole wird zunächst unabhängig von Eclipse gestartet. Geben Sie dazu im Browser den Link "ich darf keine URLs posten" ein.
Die Konsole läuft unabhängig von Ihrem Programm und muss im Normalfall nicht mehr beendet werden.

b) Um die Konsole aus Ihrem Programm ansprechen zu können, ergänzen Sie in der Header-Datei Ihrer Saugroboterklasse die Anweisung 'include "cleanerbot/World.h".
Damit steht Ihnen eine Klasse "World" zur Verfügung.
Sie benötigen zunächst nur die Methode show(double positionX, double positionY, double angle, double diameter=0.4).
Der Aufruf dieser Methode veranlasst die Konsole einen saugroboter an der angebeben Stelle mit der angegebenen Ausrichtung anzuzeigen bzw ihn an die angegebene Stelle zu bewegen.

Ergänzen Sie nun in der Implementierung ihrer Saugroboterklasse ein Attribut World* m_world mit dem Inititalwert NULL. Ergänzen Sie weiter die Methode "void setWorld(World* world)", die dem Attribut den übergebenen Wert zuweist.

Fügen Sie in der Implementierun ihrer Klasse in allen Methoden, die die Position verändern einen Aufruf der Methode show(..) des neuen Attributs m_World ein.
Rufen Sie die Methode auch on der setWorld Methode auf.

Ergänzen Sie Ihre Saugroboterklasse außerdem um einen Destruktor in dem die show-Methode von m_world mit dem Wert 0 für den Durchmesser aufgerufen wird, um den Saugroboter wieder aus der Darstellung zu entfernen.

Beachten Sie bei allen Änderungen, dass Ihre Saugroboterklasse auch dann weiter lauffähig sein soll, wenn keine "Welt" bekannt ist, dh wenn das Attribut m_world den Wert NUL hat.

c) Erweitern Sie Ihr Testprogramm aus Übung 2, Aufgabe 2c. Ergänzen Sie zu Beginn die Deklaration eines Objekts vom Typ "World" (noch bevor Sie Ihren Saugroboter deklarieren) und machen Sie das Objekt Ihrem Saugroboter mit setWOrld bekannt.
Wiederholen Sie die Steueranweisungen des Test mit ihrem Saugroboter, der sich jetzt in einer Welt bewegt und beobachten Sie die Konsole.


CVacubot.h
code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
//#ifndef CVACUBOT_H_
#define CVACUBOT_H_

#include "cleanerbot/World.h"
//#include "CPoint.h"
class CVacubot {

private:
	double m_Diameter;
	double m_X;
	double m_Y;
	double m_Alpha;
	int m_M;
	World* m_World;
	//CPoint* position;

public:
	CVacubot();
	~CVacubot();
	double getPosition();
	double getAlignment();
	double setAlignment(double Alpha);
	int setMovement(int M);
	void print();
	void setWorld(World* world);

};

//#endif /* CVACUBOT_H_ */



CVacubot.cpp
code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
#include <iostream>
#include <stdlib.h>
#define _USE_MATH_DEFINES
#include <cmath>
using namespace std;
#include "CVacubot.h"
#include "cleanerbot/World.h"
//#include "CPoint.h"

CVacubot::CVacubot()
{
	m_X=0;
	m_Y=0;
	m_Diameter=0.4;
	m_Alpha=0;
	m_M=0;
	//m_World=0;
	//position=0,0;
}

void CVacubot::setWorld(World* world)
{

World* positionX=&m_X;
World* positionY=&m_Y;
World* angle=&m_Alpha;
World* diameter=&m_Diameter;
m_World->show(m_X, m_Y, m_Alpha, m_Diameter=0.4);

return;
}

double CVacubot::setAlignment(double Alpha)
{
	m_Alpha=((Alpha*M_PI)/180)+m_Alpha;
	m_World->show(m_X, m_Y, m_Alpha, m_Diameter);
	return true;
}

int CVacubot::setMovement(int M)
{
	m_M=M;
	m_X=m_M*cos(m_Alpha)+m_X;
	m_Y=m_M*sin(m_Alpha)+m_Y;
	m_World->show(m_X, m_Y, m_Alpha, m_Diameter);
	return true;
}

double CVacubot::getAlignment()
{
	return m_Alpha;
}

double CVacubot::getPosition()
{
	return m_X;
	return m_Y;
}

void CVacubot::print()
{
	double Alpha;
	Alpha=(m_Alpha*180)/M_PI;
	m_X=(int)(m_X*100+0.5)/100.0;
	m_Y=(int)(m_Y*100+0.5)/100.0;
	cout << "Die Ausrichtung des Saugroboters beträgt: "<<m_Alpha<<" radians ("<<Alpha<<"°)"<<endl;
	cout << "Die Position (X/Y) des Saugroboters lautet: "<<m_X<<"m/"<<m_Y<<"m"<<endl<<endl;


	return;
}

CVacubot::~CVacubot()
{
	m_World->show(m_X, m_Y, m_Alpha, m_Diameter=0);
}



main.cpp
code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
// GIT-Labor
// main.h

////////////////////////////////////////////////////////////////////////////////
// Header-Dateien
#include <iostream>		// Header für die Standard-IO-Objekte (z.B. cout, cin)
#include <stdlib.h>
// TODO: Fügen Sie hier weitere benötigte Header-Dateien ein

using namespace std;	// Erspart den scope vor Objekte der
						// C++-Standard-Bibliothek zu schreiben
						// z.B. statt "std::cout" kann man "cout" schreiben
#include "CVacubot.h"
#include "cleanerbot/World.h"
//#include "CPoint.h"

// Inkludieren Sie die Header-Datei Ihrer Klasse


// Hauptprogramm
// Dient als Testrahmen, von hier aus werden die Klassen aufgerufen
int main (void)
{

	World world;
	CVacubot Saugroboter;

	Saugroboter.setAlignment(90);
	Saugroboter.setMovement(1);
	Saugroboter.print();
	Saugroboter.setAlignment(-60);
	Saugroboter.setMovement(1);
	Saugroboter.print();
	Saugroboter.setWorld;



	return 0;
}


Edit: World.h und World.cpp nach Anforderung entfernt.
Auf diesen Beitrag antworten »
eulerscheZahl

Zum Testen reicht es zwar immer noch nicht (es fehlt z.B. util/Socket.h), aber ich denke, ich kann mir so einen Überblick verschaffen. Den Teil brauchst du also nicht nachliefern.

code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
void CVacubot::setWorld(World* world)
{

World* positionX=&m_X;
World* positionY=&m_Y;
World* angle=&m_Alpha;
World* diameter=&m_Diameter;
m_World->show(m_X, m_Y, m_Alpha, m_Diameter=0.4);

return;
}

Ich fasse zusammen: wir erstellen einen Zeiger auf ein World-Objekt, geben ihm den Namen positionX und zeigen auf m_X.

code:
1:
2:
3:
4:
5:
6:
7:
World* m_World = NULL;

void CVacubot::setWorld(World* world)
{
    m_World = world;
    world->show(m_X, m_Y, m_Alpha, m_Diameter);
}

* wir brauchen in der Roboterklasse einer Verweis auf eine Welt
* bei setWorld wird die übergebene Welt als aktuelle Welt festgelegt
* wir zeigen die Welt
* die 0.4 für den Durchmesser sind ein default, falls du gar nichts angibst. Das verwendet man bei der Deklaration der Funktion, nicht beim Aufruf
* wenn eine Funktion void ist, kannst du dir das return am Ende schenken

Und bei der URL, die du nicht posten durftest, tippe ich auf 127.0.0.1:23454

edit: in der main:
Zitat:
Saugroboter.setWorld;

da fehlt ein Funktionsargument, nämlich eine World
also
code:
1:
2:
World world = new World();
Saugroboter.setWorld(world);
Auf diesen Beitrag antworten »
Tommy

Das Hauptproblem war, dass ich die Methode setWorl ganz am Anfang hätte aufrufen müssen, und dann erst die setAlignment und setMovement Methoden

code:
1:
2:
3:
4:
5:
6:
7:
8:
9:

World pworld;
CVacubot Saugroboter;
Saugroboter.setWorld(&pworld);
Saugroboter.setAlignment(90);
Saugroboter.setMovement(1);




Und natürlich wie Du auch beschrieben hast in der setWorld Methode die übergebene Welt als aktuelle Welt.

code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:

void CVacubot::setWorld(World* pWorld)
{

	m_World = pWorld;
	if (m_World!=NULL)
	m_World->show(m_X, m_Y, m_Alpha, m_Diameter);

}



Danke für Deine Hilfe !
Man da liegt echt noch ein weiter Weg vor mir ... aber das Gefühl, wenn nach Tagen des rumfrickeln endlich alles funktioniert ist echt toll :-D
Auf diesen Beitrag antworten »
Tommy

Die Aufgabe wurde mittlerweile erweitert und zwar sollen wir nun den Roboter per Eingabe steuern.
Dazu ist in der Klasse World die Methode "getKey()" implementiert.
Wir haben ein Aktivitätsdiagramm bekommen, nach dem wir diese methode implementieren sollen.
Sieht bei mir so aus:

code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:

World pworld;
CVacubot Saugi;
Saugi.setWorld(&pworld);

	do
	{
			pworld.getKey();

			cout <<"Inhalt von getKey: "<< pworld.getKey() <<endl << endl;


				if (pworld.getKey() == "up")
		{
		Saugi.setMovement(1);
		}

				if (pworld.getKey() == "left")
		{
		Saugi.setAlignment(45);
		}

				if (pworld.getKey() == "right")
		{
		Saugi.setAlignment(-45);
		}


	}
		while (pworld.getKey() != "e");
		return 0;



Läuft auch, aber ... man muss 3-4 mal dieselbe Taste drücken bis etwas passiert.
Woran liegt das ?
Auf diesen Beitrag antworten »
eulerscheZahl

Du rufst in der Schleife 5 Mal getKey auf, liest also auch 5 Buchstaben ein (richtig wäre einer Augenzwinkern )
Wenn du die Buchstaben in der richtigen Reihenfolge drückst (z.B. up, up, left, right, up), wird überhaupt nichts passieren.
code:
1:
2:
3:
String key = pworld.getKey();
if (key == "up") {...}
if (key == "right") {...}
Auf diesen Beitrag antworten »
Tommy

Saugeil ! großes Grinsen großes Grinsen
Danke Gott

Jetzt habe ich nur ein kleines Problem mit der Schleife:

code:
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:

        World pworld;
	CVacubot Saugi;
	Saugi.setWorld(&pworld);

	string key = pworld.getKey();

do
		{
				string key = pworld.getKey();

				cout <<"Eingabe: "<< key <<endl;

				if (key == "up")
					{Saugi.setMovement(1);}

				if (key == "left")
					{Saugi.setAlignment(45);}

				if (key == "right")
					{Saugi.setAlignment(-45);}

				if (key == "e")
					{break;}

		}

while (key != "e");
return 0;



Wenn ich "string key = getKey();" nicht in die Schleife packe, dann dreht sich der Roboter immer nur im Kreis. Wenn ich es in sowohl in als auch ausserhalb der Schleife stehen habe, dann funktioniert meine while Bedingung nicht mehr, ausser, ich schreibe in der while Bedingung wieder "while (get.Key() != "e")" was dann aber wieder zu mehrfachem drücken von Tasten führt.
Meine Brechstangen-Lösung war jetzt eine break Bedingung.
Ich habe irgendwo gelesen, dass ich dafür in die Hölle komme, zusammen mit "goto"-Usern LOL Hammer
Auf diesen Beitrag antworten »
eulerscheZahl

Bei deinem code legst du 2 Variablen namens key an, die in der Schleife verdeckt dann die außerhalb. Bei while wird die äußere abgefragt, die nur einmal vor Eintritt in die Schleife befüllt wird.

versuche es mal so (ungetestet):
code:
1:
2:
3:
4:
5:
string key; //Variable erzeugen, die auch außerhalb der Schleife und somit bei while gültig ist
do {
    key = pworld.getKey(); //keine neue Variable anlegen!
    if (key == ...
} while (key != "e");

Ich sehe break und continue jetzt nicht so kritisch.
Sogar goto kann nützlich sein, wenn du bei mehreren verschachtelten Schleifen aus allen raus willst und nicht nur aus der innersten.
Auf diesen Beitrag antworten »
Tommy

Läuft Tanzen

Und zwar noch besser als vorher!
Bei meiner vorherigen Lösung passierte bei der ersten Eingabe nichts.
Jetzt reagiert der Roboter sofort Daumen hoch
 
Neue Frage »
Antworten »


Verwandte Themen

Die Beliebtesten »
Die Größten »
Die Neuesten »