Vererbung von Vector<T> an NumericVector<T> Probleme mit operator* u.ä.



  • CStoll schrieb:

    cris492 schrieb:

    Alles was ein Vector kann soll auch ein NumericVector haben und können und mehr.

    Und dein Code verletzt diese Bedingung schon - ein Vector kann mit beliebigen Typen initialisiert werden, ein NumericVector nicht 😉

    Verstehe ich jetzt nicht wie Du das meinest ich kann doch bei beiden mit beliebigen Datentypen initialisieren und rechenen, oder wo geht mir das verloren?

    CStoll schrieb:

    Was hast du gegen implizite Casts? (btw, erstaunlich ist, daß du beim NumericVector genau diese Methode verwendet hast)

    Ja ich mache das auf den Rat hin auch genau so und wie gesagt recht hat er, dass macht so genau was ich will.

    CStoll schrieb:

    Gemeint war wohl eher so etwas:

    //Mult-Assign (i.a. effizienter als Multiplikation)
    Vector<T>& Vector<T>::operator*=(T val){...}
    
    //Multiplikation (nutzt op*=)
    Vector<T> operator*(const Vector<T>& rhs,T lhs)
    {
      Vector<T> ret=rhs;
      return ret*=lhs;
    }
    

    Ja das ist mir nach etwas überlegung dann auch aufgegangen wie er das gemeint haben muss. Da ich ja bei + und * usw. sowieso ein neues Objekt erzeuge kann ich auch gleich auf += und *= aufbauen.

    CStoll schrieb:

    Wenn du jeden Vector als NumericVector verwenden willst, wieso definierst du dann überhaupt zwei verschiedene Typen?

    Das liegt daran das in der Ingenieursmathematik in verschiedensten Zusammenhängen Vektoren gebraucht werden. z.B. solche die man als echte Vektoren bezeichnen würde da sie Elemente eines Vektorraums sind und man mit ihnen Gleichungssysteme lösen will und andere wie Zeitreihen die ganz andere Objekte sind aber im Rechner gleich gespeichert werden und die meisten Operatoren das selbe machen. Allen gemein ist nur das array was im Rechner dahintersteht.
    Der konkrete Anlass für mich war aber tatsächlich ein anderes Problem, wo Du bestimmt noch eine wetvolle Einsicht zu hast.
    Ich hab eine Klasse Vector und eine Klasse Matrix.
    In der Klasse Matrix gibt es einen op* der Vector = Matrix * Vector macht, also die Klasse Vector schon kenne muss.
    In der Klasse Vector gibt es jetzt eine Funktion LGS, der eine Matrix und ein Vector übergeben wird und die dann mit x.LGS(A,b) ein x findet so das A*x = b ist.
    Mit meine Kentnissen geht das so nicht. da die Klassen aufeinander aufbauen.
    Für die genannten Funktionen muss jeweils die andere Klasse schon bekannt sein.

    Gibt's da eine Trick?



  • cris492 schrieb:

    CStoll schrieb:

    Wenn du jeden Vector als NumericVector verwenden willst, wieso definierst du dann überhaupt zwei verschiedene Typen?

    Das liegt daran das in der Ingenieursmathematik in verschiedensten Zusammenhängen Vektoren gebraucht werden. z.B. solche die man als echte Vektoren bezeichnen würde da sie Elemente eines Vektorraums sind und man mit ihnen Gleichungssysteme lösen will und andere wie Zeitreihen die ganz andere Objekte sind aber im Rechner gleich gespeichert werden und die meisten Operatoren das selbe machen. Allen gemein ist nur das array was im Rechner dahintersteht.

    Jetzt verstehe ich die Zusammenhänge überhaupt nicht mehr. Wenn du nur die gleiche interne Darstellung benötigst, ist öffentliche Vererbung der falsche Weg.

    Der konkrete Anlass für mich war aber tatsächlich ein anderes Problem, wo Du bestimmt noch eine wetvolle Einsicht zu hast.
    Ich hab eine Klasse Vector und eine Klasse Matrix.
    In der Klasse Matrix gibt es einen op* der Vector = Matrix * Vector macht, also die Klasse Vector schon kenne muss.
    In der Klasse Vector gibt es jetzt eine Funktion LGS, der eine Matrix und ein Vector übergeben wird und die dann mit x.LGS(A,b) ein x findet so das A*x = b ist.
    Mit meine Kentnissen geht das so nicht. da die Klassen aufeinander aufbauen.
    Für die genannten Funktionen muss jeweils die andere Klasse schon bekannt sein.

    Gibt's da eine Trick?

    forward-Deklarationen:

    class Vector;
    class Matrix
    {
    public:
      Matrix Inv(){...}
      //nur deklariert - hier reicht der Name:
      Vector operator*(const Vector& lhs);
    };
    
    class Vector
    {
    public:
      Vector LGS(const Matrix& A,const Vector& b)
      { return A.Inv()*b; }
    };
    
    //Definition - benötigt komplette Klassendeklaration
    Vector Matrix::operator*(const Vector& lhs)
    {...}
    


  • Das nach Definition und Deklaration zu trennen und ineinander zu schachteln werde ich mal testen.

    CStoll (off) schrieb:

    Jetzt verstehe ich die Zusammenhänge überhaupt nicht mehr. Wenn du nur die gleiche interne Darstellung benötigst, ist öffentliche Vererbung der falsche Weg.

    Wieso das denn? Ist es nicht nahe liegend das ich die allen gemeinsamen Eigenschaften, hier nur die länge (im Speicher), und die allen gemeinsamen Funktionen, anzeigen, op*=, op*, op+, op() u.ä. nur einmal schreibe und dann durch Vererbung und Spezialisierung die speziellen Klassen die ich dann eigentlich erst benutzen will erzeuge? Ich dachte das ist die eigentliche Anwendung der Vererbung. Allgemeines einmal und Spezielles dann wo gebraucht implementieren.

    mfG, cris492



  • Ja, aber du bringst dort etwas durcheinander, wer von was erbt. Zeitreihen (Vector) und mathematische Vektoren (NumericVector) verwenden zwar die selbe Datendarstellung, aber ansonsten besteht keine direkte Vererbungsbeziehnung zwischen ihnen - deshalb sollten sie eher beide von einer gemeinsamen Basisklasse (BaseVector) abgeleitet werden als direkt voneinander.



  • Oh das hab ich nie erwähnt.
    Vector soll die Basisklasse sein.
    Zeitreihenvector u.ä. gibt es noch garnicht.



  • Achso. Trotzdem ist dein Design verbesserungswürdig. Öffentliche Vererbung stellt ein "ist-ein" dar, um ein "ist-implementiert-mit" bzw. "verwendet" zu kennzeichnen, verwendest du besser private Vererbung oder Aggregation.

    Btw, wenn du die Zeitreihen-Vektoren dazunimmst, wirst du dir wünschen, keine Umwandlung von Vektor nach NumericVector zu haben 😉



  • CStoll schrieb:

    Achso. Trotzdem ist dein Design verbesserungswürdig. Öffentliche Vererbung stellt ein "ist-ein" dar, um ein "ist-implementiert-mit" bzw. "verwendet" zu kennzeichnen, verwendest du besser private Vererbung oder Aggregation.

    Aber bei privater Vererbung kann ich ja meine BasisFunktionen nicht mehr so einfach durchreichen. Und was ist denn bitte Aggregation, hab ich ja noch nie gehört.

    mfG cris492



  • cris492 schrieb:

    Aber bei privater Vererbung kann ich ja meine BasisFunktionen nicht mehr so einfach durchreichen.

    Wie du gesehen hast, klappt das selbst bei öffentlicher Vererbung nicht so einfach 😉

    Und was ist denn bitte Aggregation, hab ich ja noch nie gehört.

    "hat-ein"-Beziehung (Elementzuordnung).



  • Ja, aber was da bei der öffentlichen Vererbung nicht geklappt hat, läuft nun super mit dem Konvertierungskonstrucktor. Das Problem war ja das bestimmte operatoren ein neu erzeugtes Objekt zurückgeben und der op= dann gemeckert hat.
    Es scheint mir eine gute Lösung mit dem KonvertCtor.

    Vielen Dank



  • Dann warte mal ab, bis du mit dem Zeitreihen-Vektor anfängst - mit dem Konvertierungs-Konstruktor kann es dir nämlich ganz schnell passieren, daß du verschiedene Vektortypen bunt durcheinander verrechnest.


  • Mod

    ein hinweis auf http://www.boost.org/libs/utility/operators.htm ist hier angebracht.

    eine möglichkeit, ein duplikat der vector-implementation zu vermeiden, besteht darin, diese als vector basisklasse nach dem typ des vectors zu parametrisieren:

    template<typename T, template<typename> class U>
    class VectorBase
    {
    private:
        T* vectorPtr;
        COUNTER dim;
    protected:
        VectorBase(COUNTER n, const T& initVal);
        ~VectorBase(){delete [] vectorPtr;}
    
        VectorBase(const VectorBase&);
        VectorBase& operator=(const VectorBase&);
    public:
        U<T>& operator+=(const VectorBase&); // hier wird dann ein return static_cast< U<T>& >( *this )  benötigt
        U<T>& operator-=(const VectorBase&);
    
     // usw
    
    };
    template<typename T, template<typename> class U>
    U<T> operator+(U<T> lhs, const U<T>& rhs)
    {
        return lhs += rhs;
    }
    // usw
    
    template<typename T>
    class Vector : public VectorBase< T, Vector >
    {
    // ...
    };
    
    template<typename T>
    class NumericVector : public VectorBase< T, NumericVector >
    {
    // ...
    };
    

    Vector und NumericVector sind jetzt zwei template klassen, die nichts miteinander zu tun haben, die gemeinsame representation der daten ist dennoch nur einmal implementiert. das ist auch keine perfekte lösung...


Anmelden zum Antworten