Probeklausur: 3 Dimensionale Vektoren



  • 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 Bezeichner
    
    friend 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


Anmelden zum Antworten