Probeklausur: 3 Dimensionale Vektoren
-
Neue Übungsklausur, wieder planlos

Worum es genau geht erklärt die Aufgabenstellung wohl besser:
Wer rettet Raumschiff Enterprise?
Das Haupttriebwerk von Raumschiff Enterprise ist ausgefallen. Da nur noch die Nottriebwerke
mit begrenzter Reichweite zur Verfügung stehen, muss zur Reparatur der
nächstliegende Planet angeflogen werden. Eine C++-Anwendung soll Commander
Kirk bei dieser Aufgabe unterstützen. Um die Sternpositionen im Startrek-Universum
beschreiben zu können, entwerfen Sie zuerst einen abstrakten Datentyp Vector3D, der dreidimensionale
Vektoren darstellen kann. Danach implementieren Sie die Klasse Universe, die Sternpositionen in Form von
dreidimensionalen Vektoren verwalten kann. Außerdem modellieren Sie die für diese Navigations Aufgabe
wichtigen Teile der Raumschiffklasse NX, zu der Raumschiff Enterprise gehört.Das war erstmal die allgemeine, jetzt die zur 1. Aufgabenstellung:
1 Abstrakter Datentyp Vector3D (Summe 100 Pkt.)
die Schnittstelle und die Funktionalität der Klasse Vector3D ergeben sich aus den Anweisungen und den Kommentaren
der Funktion testClassVector3D. Diese Funktion soll die Anwendung aufrufen, um die Funktion
Test durchzuführen. Die Konstruktoren werfen eine "std::runtime_error"-Ausnahme, sobald mehr als
1010 Vektoren gültig sind. Fangen Sie diese am Ende der Anwendung auf.Dazu gibt's folgenden folgende globale Funktion, die wir über der main() aufrufen müssen:
void testClassVector3D() { // Die Komponenten (v1,v2,v3) eines Vector3D-Objekts sind private und vom Typ double. double a=1., b=2., c=3.; // Den Funktionsumfang der Klasse beschreiben die folgenden Anweisungen und Kommentare: // Die Klasse kennt zu jeder Zeit die Anzahl der von ihr erzeugten gültigen Objekte cout << "Anzahl Objekte: " << Vector3D::getObjCount() << endl; // Ausgabe 0 // Der/die Konstruktor(en) können wie folgt angewendet werden: Vector3D u; // erzeugt den 3D-Vector u=(0,0,0) [5 Pkt.] Vector3D v(a); // erzeugt den 3D-Vector v=(a,0,0) [5 Pkt.] Vector3D w(a,b); // erzeugt den 3D-Vector w=(a,b,0) [5 Pkt.] Vector3D x(a,b,c); // erzeugt den 3D-Vector x=(a,b,c) [5 Pkt.] Vector3D y(x); // erzeugt den 3D-Vector y=(x1,x2,x3) [10 Pkt.] cout << "Anzahl Objekte: " << Vector3D::getObjCount() << endl; // Ausgabe 5 [10 Pkt.] // Alle Ausgaben erfolgen stets mit zwei Nachkommastellen cout << "u = " << u << endl; // gibt (0.00,0.00,0.00) aus cout << "v = " << v << endl; // gibt (1.00,0.00,0.00) aus cout << "w = " << w << endl; // gibt (1.00,2.00,0.00) aus cout << "x = " << x << endl; // gibt (1.00,2.00,3.00) aus cout << "y = " << x << endl; // gibt (1.00,2.00,3.00) aus [20 Pkt.] // v.length gibt sqrt(v12+v22+v32) mit Typ double zurück; x wird nicht verändert. cout << "x.length() = " << x.length() << endl; // Ausgabe: 3.74 (= sqrt(a2+b2+c2) ); // x wird nicht geändert. [10 Pkt.] // Skalarprodukt: v*w = (v1*w1+v2*w2+v3*w3) vom Typ double; // v=(v1,v2,v3) und w=(w1,w2,w3) sind danach unverändert. cout << " v * w = " << v*w << endl; // Ausgabe 14.00 [15 Pkt.] // Vektoraddition: v+w = (v1+w1, v2+w2, v3+w3) // v=(v1,v2,v3) und w=(w1,w2,w3) sind danach unverändert cout << " x + x = " << x+x << endl; // Ausgabe (2.00,4.00,6.00) [10 Pkt.] // Vektorsubtraktion: v-w = (v1-;w1, v2-w2, v3-w3) // v=(v1,v2,v3) und w=(w1,w2,w3) sind danach unverändert cout << " x - x = " << x-x << endl; // Ausgabe (0.00,0.00,0.00) [5 Pkt.] }Es steht ja geschrieben, dass meine Klasse Vector3D 3-Dimensionale-Vectoren darstellen können soll. Ich hab aber keine Ahnung wie man so einen Vektor in einer Klasse initialisieren kann... hab's mit allen mir möglichen Varianten probiert. Dann kam mir noch der Gedanke, dass man evtl garkeinen 3-Dimensionalen Vektor braucht, sondern die Klasse mit ihren 3 Objekten (v1,v2,v3) selbst dieser Vektor ist?
Zumindest habe ich dann nach diesem Schema weiterprgrammiert:
Header
#pragma once #include <vector> class Vector3D { private: double v1_; double v2_; double v3_; //std::vector<std::vector<std::vector<double>>> matrix; rein oder raus? static int numberOfObjects; public: Vector3D(double v1_=0.00,double v2_=0.00,double v3_=0.00); Vector3D(const Vector3D& original); ~Vector3D(void); static int getObjCount(); };CPP
#include "Vector3D.h" int Vector3D::numberOfObjects=0; Vector3D::Vector3D(double v1,double v2,double v3) { v1_ = v1; v2_ = v2; v3_ = v3; this->numberOfObjects++; } Vector3D::Vector3D(const Vector3D& original) { v1_ = original.v1_; v2_ = original.v2_; v3_ = original.v3_; this->numberOfObjects++; } Vector3D::~Vector3D(void) { this->numberOfObjects--; } int Vector3D::getObjCount() { return numberOfObjects; }Glaubt ihr, dass mein Ansatz so in etwa stimmen kann? Oder bin ich mal wieder aufm dem Holzweg?
Danke im voraus
MfG
Sorex
-
Ja, der Ansatz sieht schonmal gut aus, eventuell könnte man die Konstruktoren mit Initialisierungslisten ein wenig optimieren.
PS: Die Anforderung mit der Anzahlbeschränkung hast du noch nicht umgesetzt - auch wenn ich sowas für sinnlos halte

-
Initalisierungslisten haben wir leider noch nicht durchgenommen, daher verboten^^
Hab die Anzahlbeschränkung mal so implementiert (im Konstruktor):
if (this->numberOfObjects > 1010) throw (std::runtime_error("invalid range")); else this->numberOfObjects++;Ist das eurer Meinung nach ausreichend oder wär da ein try catch - Block attraktiver?
-
Nein, das sieht so richtig aus - der try-catch-Block steht am anderen Ende der Verarbeitung, wo du auf den Fehler reagieren mußt (das entspricht dem Teil "Fangen Sie diese am Ende der Anwendung auf." der Aufgabe).
-
Sorex schrieb:
Initalisierungslisten haben wir leider noch nicht durchgenommen, daher verboten^^
Hab die Anzahlbeschränkung mal so implementiert (im Konstruktor):
if (this->numberOfObjects > 1010) throw (std::runtime_error("invalid range")); else this->numberOfObjects++;Das sieht für mich soweit ganz gut aus. Das else kannst du allerdings weglassen, da dieses eh nicht mehr erreicht wird, wenn das throw ausgeführt wird.
Außerdem würde ich noch die überflüssigen Klammern beim throw weglassen.Das sollte dann so aussehen:
if (this->numberOfObjects > 1010) throw std::runtime_error("invalid range"); this->numberOfObjects++;Sorex schrieb:
Ist das eurer Meinung nach ausreichend oder wär da ein try catch - Block attraktiver?
Einen try-catch-Block im Konstruktor macht keinen Sinn. Du kannst dort auf den Fehler eh nicht weiter reagieren. Außerdem sagt ja die Aufgabenstellung bereits:
Die Konstruktoren werfen eine "std::runtime_error"-Ausnahme, sobald mehr als
1010 Vektoren gültig sind. Fangen Sie diese am Ende der Anwendung auf.
-
Alles klar, dann mach ich das gleich.
Hab momentan aber einen weiteren Fehler.Bin gerade am Überladen des Rechenoperators "-", bekomme aber diese Fehlermeldung:
warning C4566: Das durch den universellen Zeichennamen "\u2010" dargestellte Zeichen kann in der aktuellen Codepage (1252) nicht dargestellt werden. error C2065: 'x-x': nichtdeklarierter Bezeichnerfriend Vector3D operator-(const Vector3D& m1,const Vector3D& m2); Vector3D operator-(const Vector3D& m1,const Vector3D& m2) { //v-w = (v1-w1, v2-w2, v3-w3) Vector3D result(m1.v1_-m2.v1_, m1.v2_-m2.v2_, m1.v3_-m2.v3_); return result; }Der + und * Operator Funktionieren jeweils, der * Operator liefert aber ein anderes Ergebnis
cout << " v * w = " << v*w << endl; // Ausgabe 14.00 [15 Pkt.]Das soll dabei rauskommen, ich bekomme eine 1.
friend double operator*(const Vector3D& m1,const Vector3D& m2); double operator*(const Vector3D& m1,const Vector3D& m2) { //v1*w1+v2*w2+v3*w3 double result = (m1.v1_*m2.v1_ + m1.v2_*m2.v2_ + m1.v3_*m2.v3_); return result; }
-
Ich rate mal ins Blaue und sag: Du hast den Code für die Deklaration von deinem operator - aus einem pdf oder sonstwo rauskopiert. In dem Quelldokument wurde fälschlicherweise nicht das Minus-Zeichen sondern ein Bindestrich (U+2010) gesetzt (sieht so ähnlich aus, ist aber ein anderes Zeichen) und mit dem kann der Compiler natürlich nix anfangen. Also lösch das "-" und ersetz es durch ein richtiges -

-
Das war tatsächlich der Fehler, danke
In der Klausur hätte ich mich dumm und dämlich gesucht^^
-
Sorex schrieb:
cout << " v * w = " << v*w << endl; // Ausgabe 14.00 [15 Pkt.]Das soll dabei rauskommen, ich bekomme eine 1.
friend double operator*(const Vector3D& m1,const Vector3D& m2); double operator*(const Vector3D& m1,const Vector3D& m2) { //v1*w1+v2*w2+v3*w3 double result = (m1.v1_*m2.v1_ + m1.v2_*m2.v2_ + m1.v3_*m2.v3_); return result; }Also wenn meine Mathe-Kenntnisse mich nicht völlig im Stich lassen, ist (1,0,0)*(1,2,0)=1, also sieht dein Ergebnis korrekt aus.
-
... wer gucken und rechnen kann ist wohl klar im Vorteil

-
2 Das Startrek-Universum (Summe 50 Pkt.)
das Universum besteht aus 1000 Sternen und Planeten, die von 0-999 durchnummeriert sind. Ihre Koordinaten
werden in einem dreidimensionalen rechtwinkligen Koordinatensystem beschrieben. Im Ursprung dieses Koordinatensystems
(Position (0,0,0) ) fand der Urknall statt. Wegen einer hyperstrukturalen Anomalie haben sich
danach die Himmelskörper regelmäßig im Raum verteilt:
Die X Koordinate ergibt sich aus der Einerstelle der Sternnummer.
Die Y Koordinate ergibt sich aus der Zehnerstelle der Sternnummer
Die Z Koordinate ergibt sich aus der Hunderterstelle der Sternnummer
der Standardkonstruktor der Klasse Startrek_Universe lässt sich die Positionen der 1000 Sterne nach dieser
Vorgabe von einer globalen Funktion getStarPosition [10 Pkt.] berechnen, speichert diese für jeden Stern
in einem Vektorobjekt und legt dieses in einem geeigneten C-Array ab. Das C-Array hat er zuvor dynamisch,
d.h. zur Laufzeit erzeugt [10 Pkt.].
Die Abfrage der Sternpositionen erfolgt mit dem Indexoperator "[ ]" [10 Pkt.]. Mit den Anweisungen
Startrek_Universe universe;
Vector3D position = universe[111];
erhält man die Position des Himmelskörpers 111. Wenn versucht wird, auf einen nicht existierenden Stern zu
zugreifen, wird eine std::invalid_argument Exception ausgeworfen.
Hier ihre Aufgabe:
1. Erzeugen Sie das Universum.
2. Sortieren Sie die Sternpositionen nach der Z-Koordinate, bei gleicher Z-Koordinate nach der YKoordinate,
und bei gleicher Z- und Y-Koordinate nach der X-Koordinate jeweils in absteigender Folge.
3. Geben Sie die so sortierten Sternpositionen aus. [20 Pkt.]Bin jetzt am 2. Aufgabenteil angelangt, bekomme aber, da ich jedes mal ein Objekt vom Typ Vector3D erzeugen muss und somit mein numberOfObjects erhöht wird, einen Runtime_Error. Wie könnte ich das am besten umgehen?
#pragma once #include "Vector3D.h" class Startrek_Universe { private: Vector3D* matrix_; int matrixCapacity_; int matrixSize_; public: Startrek_Universe(int matrixCapacity_=0, int matrixSize_=0); ~Startrek_Universe(void); void push_back(Vector3D& m); void allocateMemory(); Vector3D& Startrek_Universe::operator[](int value); };#include "Startrek_Universe.h" #include "Vector3D.h" Vector3D getStarPosition(int value) { double x=0.0; double y=0.0; double z=0.0; int valueTmp = value; if (value <10) { x = value; } else if (value > 9 && value < 100) { x = valueTmp%10; valueTmp /= 10; y = valueTmp%10; } else { x = valueTmp%10; valueTmp /= 10; y = valueTmp%10; valueTmp /= 10; z = valueTmp%10; } Vector3D position(x,y,z); return position; } Startrek_Universe::Startrek_Universe(int matrixCapacity, int matrixSize) { Vector3D v3D; matrix_ = nullptr; matrixCapacity_ = matrixCapacity; matrixSize_ = matrixSize; for(int i=0;i<1000;i++) { v3D = getStarPosition(i); push_back(v3D); } } Startrek_Universe::~Startrek_Universe(void) { delete[] matrix_; matrix_ = nullptr; } void Startrek_Universe::push_back(Vector3D& m) { if(matrixSize_ >= matrixCapacity_) allocateMemory(); matrix_[matrixSize_++] = m; } void Startrek_Universe::allocateMemory() { if(matrix_==0) matrixCapacity_ = 1; else matrixCapacity_ *= 2; Vector3D* matrixTmp = nullptr; matrixTmp = new Vector3D[matrixCapacity_]; for(int i=0;i<matrixSize_;i++) { matrixTmp[i] = matrix_[i]; } delete[] matrix_; matrix_ = matrixTmp; } Vector3D& Startrek_Universe::operator[](int value) { return matrix_[value]; }
-
Also so wie ich die Aufgabenstellung verstehe ist es nicht nötig das Array dynamisch wachsen zu lassen. Mach einfach im new[1000] und gut.
Dass es mit dynamisch wachsen lassen nicht geht (wegen des 1010 Limits) siehst du ja selbst."Dynamisch erzeugt" heisst nur, dass du new[] verwenden sollst. Nicht dass sich irgendwas dynamisch anpassen muss.
Davon abgesehen...
Habt ihr "unsigned" Typen schon gemacht? size_t? Und wie sieht's mit const aus?
-
Übrigens fehlen deiner Universum-Klasse zwei wichtige Methoden: Kopier-Konstruktor und Zuweisungsoperator. Sobald der Compiler irgendeine Veranlassung sieht, die zu verwenden, wird er eigene definieren - die dann zu SegFaults, Double Deletion und Speicherlecks führen werden.
Kleinere Anmerkungen:
- die Fall-Unterscheidungen in der getStarPosition() kannst du weglassen, bestenfalls reicht eine Überprüfung, ob der übergebene Wert im Bereich 0..999 liegt
- die Parameter des Konstruktors sind völlig sinnfrei (und wenn jemand auf die Idee kommt, dort etwas einzutragen, fliegt dir das erste push_back mit einem SegFault um die Ohren). Wenn überhaupt, würde ich dort nur die Universum-Größe (mit Defaultwert 1000 nach der Aufgabenstellung) übergeben
- die Zuweisung "matrix_=nullptr;" im Destruktor ist nutzlos - sowas macht man nur, wenn der betroffene Zeiger nach dem delete noch eine Weile überleben könnte.
- willst du die Sterne (in Teilaufgabe 3) eigentlich in-place sortieren oder eine sortierte Kopie der Sternenliste anlegen? Im ersten Fall stimmt hinterher die Zuordnung der Position zu den Sternennummern nicht mehr, im zweiten Fall bekommst du echte Probleme mit der Anzahlbegrenzung.
-
Mir ist die Regel der großen Drei bekannt, aber wirklich klar ist mir das ganze noch nicht. Wann und warum würde denn der Compiler versuchen auf beide genannten Methoden zuzugreifen?
- die Parameter des Konstruktors sind völlig sinnfrei (und wenn jemand auf die Idee kommt, dort etwas einzutragen, fliegt dir das erste push_back mit einem SegFault um die Ohren). Wenn überhaupt, würde ich dort nur die Universum-Größe (mit Defaultwert 1000 nach der Aufgabenstellung) übergeben
Die Idee dahinter war eigentlich, dass die Kapazität und Größe des Array initialisiert und definiert werden. Wär hier static eine Lösung das ganze anders zu realisieren?
- willst du die Sterne (in Teilaufgabe 3) eigentlich in-place sortieren oder eine sortierte Kopie der Sternenliste anlegen? Im ersten Fall stimmt hinterher die Zuordnung der Position zu den Sternennummern nicht mehr, im zweiten Fall bekommst du echte Probleme mit der Anzahlbegrenzung.
So weit hab ich noch nicht gedacht. Bei deiner Schilderung der möglichen Probleme werde ich aber sicher wieder drauf zurückkommen

-
Sorex schrieb:
Mir ist die Regel der großen Drei bekannt, aber wirklich klar ist mir das ganze noch nicht. Wann und warum würde denn der Compiler versuchen auf beide genannten Methoden zuzugreifen?
Der Compiler erzeugt die Kopier-Methoden immer dann, wenn er der Meinung ist, ein Objekt kopieren zu müssen (z.B. bei expliziter Kopier-Initialisierung oder bei Parameterübergabe als Wert). Ob das für dein Programm relevant ist, kann ich nicht beurteilen, aber es ist immer besser darauf vorbereitet zu sein.
- die Parameter des Konstruktors sind völlig sinnfrei (und wenn jemand auf die Idee kommt, dort etwas einzutragen, fliegt dir das erste push_back mit einem SegFault um die Ohren). Wenn überhaupt, würde ich dort nur die Universum-Größe (mit Defaultwert 1000 nach der Aufgabenstellung) übergeben
Die Idee dahinter war eigentlich, dass die Kapazität und Größe des Array initialisiert und definiert werden. Wär hier static eine Lösung das ganze anders zu realisieren?
Die Idee klingt gut, die Umsetzung ist es nicht. Überleg dir mal was passiert, wenn du dort eine Kapazität von 500 (und die Default-Größe von 0) angibst: der Konstruktor belegt die Kontrollvariablen mit diesen Werten und den Datenzeiger mit NULL, danach ruft er push_back() auf. Die if()-Bedingung dort schlägt nicht zu, also wird auch kein Speicher angefordert und du schreibst das erste Element nach NULL -> SegFault.
Worauf ich hinaus wollte: Die Angaben für Größe/Kapazität sind zu wichtig, um sie einfach als Parameter zu übergeben. Wenn du überhaupt einen Parameter entgegennehmen willst, dann die Gesamtzahl an Sternen, die du letztendlich in das Universum packen willst.
-
Habs jetzt mal doch über static gemacht, da mir momentan keine andere lösunng eingefallen ist.. hänge aber gerade wie du schon vermutet hast am Sortieren.
Es heißt ja in der Aufgabenstellung:
Sortieren Sie die Sternpositionen nach der Z-Koordinate, bei gleicher Z-Koordinate nach der YKoordinate, und bei gleicher Z- und Y-Koordinate nach der X-Koordinate jeweils in absteigender Folge.Jetzt will mir nicht einfallen wie man das realisieren könnte.. kann ja in der Überladung des <-operators keine if - else Anweisungen benutzen oder? Zumindest bekomme ich da immer den Error "invalid operator<"
Hab's momentan so, kann mir aber nicht vorstellen, dass das richtig ist:
bool operator<(const Vector3D& m1,const Vector3D& m2) { return (m1.v3_) < (m2.v3_); }Zugehörige Sortierfunktion:
void Startrek_Universe::sortMatrix() { std::sort(&matrix_[0],&matrix_[1000]); std::reverse(&matrix_[0],&matrix_[1000]); }-edit-
hat sich erledigt