Vektorenaddition



  • Hi erst ma!

    Ich wollte zwei Vektoren addieren

    Mainaufruf:
    ------------
    Vector v3 = v1.addElements(v2);

    vector.h
    ---------
    virtual Vector addElements(const Vector &vReference);

    vector.cpp:
    -----------
    Vector Vector::addElements(const Vector &vReference){
    Vector temp(3,0.0);
    for(int i=0;i<vReference.n_;++i) temp.setElement(i, getElement(i)+vReference.getElement(i));
    return temp;
    }//An dieser Stelle löscht der Destruktor den temp und in Main kommt nur Müll an. Was mache ich falsch? Danke für eure Statements

    Gruß Snoopy



  • könntest du vielleicht etwas genaueres schreiben? (und nächstes mal bitte tags verwenden)

    wenn 2 vektoreb addiert werden, dann addiert man doch die einzelnen elemente oder?
    warum überlädst du dann nicht einfach deinen +operator und +=operator?
    fände ich in diesem fall besser... wenn du sie noch nicht überladen kannst, dann ändere deine funkion ab:

    Vector Vector::addElements(const Vector &vReference)
    {
    Vector temp(vReference.n_,0.0);          //erstellt er einen vektor mit 3 zeilen mit jeweils dem wert 0.0?
    for(int i=0;i<vReference.n_;++i) 
        temp[i] += vReference[i];           //die jeweiligen werte werden addiert       
    return temp;
    }
    

    aber ehrlich gesagt, macht diese funktion von dir keine sinn, da man nur einen vector angibt und deine funktion den selben vektor zurückliefert...



  • Hallo,
    Wir warten alle nur darauf _deinen_ Post beantworten zu dürfen 🙄

    Ansonsten: Hast du dir eine eigene vektor-Klasse geschrieben? Wenn nein, std::vector besitzt keine Methode addElements(); (Und ich würde diese Klasse auch nicht direkt verändern...)

    Lösungsvorschläge:
    1). Du kannst dir eine Klasse (z.B. myvec) schreiben, welche von std::vector erbt und dann in myvec die Methode addElements() definieren.
    2). Wenn das zu "aufgebläht bist, dann vielleicht eher so etwas:

    #include <iostream>
    #include <vector>
    using namespace std;
    
    // Funktioniert nur für Typen bei denen der +=-Operator überladen ist...
    template<class T>
    vector<T> addElements(const vector<T>& v1, const vector<T>& v2)
    {
            vector<T> r(v1);
            for(unsigned int i = 0; i < v1.size(); ++i)
                    r[i] += v2[i];
            return r;
    }
    
    int main()
    {
            vector<int> v1(5, 9), v2(5, 9), v3;
    
            v3 = addElements(v1, v2);
            for(unsigned int i = 0; i < v1.size(); ++i)
                    cout << v3[i] << ", ";
    
            return 0;
    }
    

    3). Schreib deine eigene vektor-Klasse...

    Caipi



  • ... deine Funktion erzeugt doch aber einen neuen Vektor (temp) den sie dann mit dem vReference addiert, oder seh ich das Falsch. Meine Funktion addiert this+vReference und legt die addierten Werte auf temp. Laut Debugger stimmen diese Werte in temp auch. Nur leider werden die sobald ich die Klasse Vector verlasse und zurück in Main gehe zerstört.
    Aber danke für deinen Tip mit den Konstruktorparametern, dass sieht doch noch etwas eleganter aus, als meine harten Werte.



  • zu dir Caipi, ja ich habe eine eigene Class vector im eigenst dafür erstellten namespace BaseVector liegen. Von der C++ Typischen Klasse Vector darf ich nicht erben. Ich soll mir selbst Funktionen dafür überlegen. Es handelt sich hierbei wieder um eine sinnlose Beschäftigungstherapie unseres Dozenten, die einein nur davon abhält für andere Vorlesungen zu pauken.
    Um einen Detailreichen überblick über die Aufgaben zu geben:

    Aufgabe
    Gegeben ist das folgende Programm:
    #include <iostream>
    #include "vector.h"
    using namespace std;
    using namespace BaseVector;
    int main(){
    Vector v1(3,2.0),v2(3,3.0);
    Vector v3 = v1.addElements(v2);
    cout << v3 << endl;
    Vector v4 = v1.multiplyElements(v2);
    cout << v4 << endl;
    v1.setElement(0,0.5);
    v2.setElement(2,0.5);
    double d = v1.scalarProduct(v2);
    cout << d << endl;
    cout << v1.n() << endl;
    cout << v1.getElement(0) << endl;
    v1 = v2;
    return 0;
    }
    Implementierten Sie alle erforderlichen Klassen, Operatoren und Funktionen, damit das obenstehende
    Programm fehlerfrei kompiliert und ausgeführt werden kann.
    Wenn Sie alles richtig implementiert haben, erzeugt das Programm bei seiner Ausführung folgende
    Ausgabe:
    Vector(5,5,5)
    Vector(6,6,6)
    8.5
    3 0.5
    Bitte beachten Sie folgende Hinweise:
    • Teilen Sie den von Ihnen implementierten Code in eine Header- und eine Implementierungsdatei auf .
    • Die Memberfunktionen der von Ihnen implementierten Klassen sollen alle über späte Bindung verfügen.
    • addElements() addiert die Werte zweier Vektoren elementweise und gibt das Ergebnis als neuen Vektor zurück.
    • multiplyELements() multipliziert die Werte zweier Vektoren elementweise und gibt das Ergebnis als neuen Vektor zurück.
    • scalarProduct() berechnet das Skalarprodukt zweier Vektoren und gibt es als double-Wert zurück.
    • Verwenden Sie weder in der von Ihnen erstellten Headerdatei noch in der Implementierungsdatei die Anweisung "using namespace std;". Greifen Sie vielmehr ausschließlich vollständig qualifiziert auf Bezeichner des Namensraums std zu.



  • für jemanden der reine Informatik studiert oder wer sich anderweitig langweilt und deswegen ausschließlich in C++ übt, eine sicherlich leichte Aufgabe. Leider studiere ich Wirtschaftsinformatik und die Vorlesungen in Programmierung werden mir zunehmend unverständlicher und Kryptischer. Ich bin euch daher für eure Hilfe sehr dankbar.
    Und kurze Anmerkung: Was versteht ihr unter tags?



  • Zu den Tags: http://www.c-plusplus.net/forum/viewtopic-var-t-is-59601.html
    Ansonsten: Zeig uns doch mal deine bisherige _komplette_ Klasse. (In Codetags)

    Caipi



  • die main in der Aufgabestellugen kennst du ja,
    hier die Header Datei:

    #include <iostream>
    namespace BaseVector{
    	class Vector{
    	public:
    		Vector();
    		Vector(int n, double wert);
    		~Vector();
    		virtual const double& getElement(unsigned int i) const;
    		virtual void setElement(unsigned int i, const double value);
    		virtual const int& n();
    		virtual int groesse() const;
    		virtual void ausgeben(std::ostream& os) const;
    		virtual Vector addElements(const Vector &vReference);
    		virtual Vector multiplyElements(const Vector &vReference);
    		virtual double scalarProduct(const Vector &vReference);
    	private:
    		int n_;
    		double wert_;
    		double * elemente_;
    	};
    	std::ostream& operator  <<(std::ostream& os, const Vector& outVec);
    }
    #endif //VECTOR_H
    

    und hier die der Inhalt der cpp:

    #include "vector.h"
    
    namespace BaseVector{
    
    	Vector::Vector():elemente_(NULL),n_(NULL),wert_(NULL){};
    
    	Vector::Vector(int n,double wert):elemente_(NULL),n_(n),wert_(wert) {
    		elemente_ = new double[groesse()];
    		for(int i=0;i<n_;++i){
    			elemente_[i]=wert_;
    		}
    	};
    
    	Vector::~Vector() {
    		delete[] elemente_;
    	};
    
    	const double& Vector::getElement(unsigned int i) const {
    		return elemente_[i];
    	}
    
    	void Vector::setElement(unsigned int i, const double value) {
    		elemente_[i] = value;
    	}
    	const int& Vector::n(){
    		return n_;
    	}
    
    	Vector Vector::addElements(const Vector &vReference){
    		Vector temp(3,0.0);
    		for(int i=0;i<vReference.n_;++i) temp.setElement(i, getElement(i)+vReference.getElement(i));
    		return temp;
    	}
    
    	Vector Vector::multiplyElements(const Vector &vReference){
    		Vector temp(3,0.0);
    		for(int i=0;i<vReference.n_;++i) temp.setElement(i, getElement(i)*vReference.getElement(i));
    		return temp;
    	}
    
    	double Vector::scalarProduct(const Vector &vReference){
    		double value = 0.0;
    		for(int i=0;i<vReference.n_;++i) value += getElement(i)*vReference.getElement(i);
    		return value;
    	}
    
    	void Vector::ausgeben(std::ostream& os) const {
    		os << elemente_[0] << ',' << elemente_[1] << ',' << elemente_[2];
    	}
    
    	int Vector::groesse() const {
    		return n_;
    	};
    
    	std::ostream& operator<<(std::ostream& os, const Vector& outVec){
    		std::ostream::sentry VorUndNacharbeiten(os);
    		if(VorUndNacharbeiten) outVec.ausgeben(os);
    		return os;
    	}
    }
    


  • Hier stand Quark.

    Caipi



  • ich wurde grad drauf hingewiesen, dass mir der Kopierkonstruktor fehlt und ich somit die KOF nicht eingehalten habe, was den Fehler verursacht. Ich werde ihn mal noch einpflegen und dann schau ich mal weiter 🙂
    trotzdem danke für eure Postings, jetzt weiß ich wenigstens schon mal wie ich tagger 😉
    Freue mich trotzdem über jede antwort, die mir hilft den Code sicherer zu gestallten. Wenn sich jemand mit asserts und ihrer Verwendung auskennt, dann immer her mit dem Imput. Ich stell mir jetzt noch ne Puddl Cachacha an den Tisch und dann kann der Abend beginnen.



  • nein, leider erzeugt das addieren und multiplizieren müll in der Rückgabe, weil es wie gesagt den Speicher per Destruktoraufruf frei gibt. Was benutzt du für einen Compiler? Vielleicht erklärt das auch, dass er bei dir durchläuft.

    Great thanxxx for answers
    Curry



  • curry schrieb:

    nein, leider erzeugt das addieren und multiplizieren müll in der Rückgabe, weil es wie gesagt den Speicher per Destruktoraufruf frei gibt. Was benutzt du für einen Compiler? Vielleicht erklärt das auch, dass er bei dir durchläuft.

    Great thanxxx for answers
    Curry

    g++. Aber wie du bereits selbst geschrieben hast, fehlt der Copy-Ctor. Dieser sollte etwa so aussehen:

    Vector::Vector(const Vector& vec)
    {
          n_ = vec.n_
          elemente_ = new double[n_];
          for(unsigned int i = 0; i < n_; ++i)
                  elemente_[i] = vec.elemente_[i];
    }
    

    Caipi



  • Vector::Vector(const Vector& copyVector):elemente_(NULL),n_(copyVector.groesse()){
    		elemente_ = new double[groesse()];
            for (int i = 0; i < groesse(); ++i)
                elemente_[i] = copyVector.elemente_[i];
    	};
    

    das hier hatte mir noch gefehlt Caipi, jetzt funzts.
    Jetzt kann ich auch befriedigt ins Bett gehen. Noch eine kurze Frage an dich: Was studerst du und hast du ICQ? Für den Fall, dass man mal noch andere Fragen haben sollge.



  • curry schrieb:

    Vector::Vector(const Vector& copyVector):elemente_(NULL),n_(copyVector.groesse()){
    		elemente_ = new double[groesse()];
            for (int i = 0; i < groesse(); ++i)
                elemente_[i] = copyVector.elemente_[i];
    	};
    

    So in der Art wie ich geschrieben hab ;). Trotzdem eine Frage dazu: Warum setzt du den Zeiger elemente_ auf Null, wenn du ihm direkt danach eine Adresse zuweist?

    das hier hatte mir noch gefehlt Caipi, jetzt funzts.
    Jetzt kann ich auch befriedigt ins Bett gehen. Noch eine kurze Frage an dich: Was studerst du und hast du ICQ? Für den Fall, dass man mal noch andere Fragen haben sollge.

    Ich studiere garnichts. Gehe in die Schule... Desweiteren kannst du doch deine Fragen hier stellen, oder? 😕

    Caipi



  • ich setze ihn aus dem Grund auf NULL, weil stell dir vor dein Programm kommt aus irgend einer fehlerhaften Implementierung ins Rudern und eine Elementzuweisung kommt nicht mehr zustande, wo greift der Destruktort dann hin? Ins Leere, richtig, und dass was er dabei an Daten versehentlich löschen kann, kann zu schweren Fehlern führen.



  • das mit dem Fragen hier stellen, ist immer so eine lässtige angelegenheit mit dem warten auf Antworten. Einige der hier anwesenden halten sich gerne für Überflieger und Posten dementsprechend völlig unzureichende überhebliche Antworten. Daher ist mir's lieb einen Stamm an C++ Usern zu Adden auf den man verlässlich in Problemsituaionen zurückgreifen kann.

    Deswegen der ausweg ins ICQ



  • curry schrieb:

    ich setze ihn aus dem Grund auf NULL, weil stell dir vor dein Programm kommt aus irgend einer fehlerhaften Implementierung ins Rudern und eine Elementzuweisung kommt nicht mehr zustande, wo greift der Destruktort dann hin?

    Wo greift der Dtor denn jetzt hin? 😉

    Ins Leere, richtig,

    So auch jetzt.

    und dass was er dabei an Daten versehentlich löschen kann, kann zu schweren Fehlern führen.

    Stimmt.

    Es ist nicht so, dass wenn du einem Zeiger 0 setzt, nichts mehr schief gehen kann. Im Gegenteil. Wenn du einen Zeiger, der auf 0 zeigt, dereferenzierst, gibt es ziemlich sicher einen Crash, da 0 nie eine gültige Adresse repräsentiert.
    Warum setzt man nun aber den Zeiger gleich 0? Das tut man deswegen, da man vor dem dereferenzieren oder freigeben überprüft ob dieser gleich 0 ist, und sofern diese Bedingung zutrifft man den Speicher _nicht_ dereferenziert oder freigibt...

    Caipi



  • na das klingt doch auch nicht schlecht 🙂 wieder was gelernt.
    Dann mach dir mal noch nen schönen Abend. Wenn du lust hast dich noch bisschen mehr über C++ Relevante Sachen auszutauschen hier meine ICQ addi: 70049002
    in diesem Sinne: N8

    Gruß Curry



  • ● Der Standardkonstruktor beschafft und initialisiert dynamische
    Anteile
    ● Handelt es sich um dynamischen Speicher wird der entsprechende
    Zeiger in der Initialisiererliste mit NULL initialisiert
    ● Der nötige Speicher wird im Konstruktor mit new oder new[] beschafft
    ● Schlägt die Speicherbeschaffung fehl, enthält der Zeiger den Wert NULL
    ● Ein anschließender Destruktoraufruf, der den Zeiger mit delete
    oder delete [] freigibt, bleibt dann ohne negative Folgen
    ● Ein NULL-Zeiger darf beliebig oft freigegeben werden
    ● Dynamischen Speicher keinesfalls in der Initialisiererliste beschaffen!
    ● Im Falle eines Fehlschlags hätte der Zeiger einen Wert ungleich NULL
    ● Ein späterer Destruktoraufruf mit delete oder delete[] hätte dann
    negative Folgen

    So stands im Skriptum, man darf demzufolge einen solchen Zeiger dereferenzieren.

    Gruß Curry



  • curry schrieb:

    ● Schlägt die Speicherbeschaffung fehl, enthält der Zeiger den Wert NULL

    Diese Info ist veraltet.
    Nach aktuellem C++ Standard wird dem Zeiger nicht der Wert 0 zugewiesen sondern eine Exception geworfen...

    So stands im Skriptum, man darf demzufolge einen solchen Zeiger dereferenzieren.

    Dereferenzieren nicht, wenn überhaupt freigeben...
    Vergl.

    #include <iostream>
    using namespace std;
    
    int main()
    {
          int* p = 0;
          cout << *p << endl;
    }
    

    (oder hab ich was überlesen?).

    Jedoch würde mich das mit dem "auf einen Null-Zeiger darf beliebig oft delete angewendet werden" auch mal interessieren. Ist das nach aktuellem Stadard legal?
    Ich habe nämlich dumpf in Erinnerung, dass man nur Speicher der mit new erzeugt wurde wieder freigeben darf und wenn nach aktuellem Standard bei einem Fehlschlag von new der Zeiger nicht gleich null gesetzt wird, dann ist ein Null-Zeiger ja nicht mit new erzeugt, oder? (Sorry, bin Noob :))
    Kann mich da jemand aufklären? :).

    Caipi


Anmelden zum Antworten