Vererbung von Vector<T> an NumericVector<T> Probleme mit operator* u.ä.
-
cris492 schrieb:
Der NumericVector<T>::operator= meldet sich genauso.
nehme ich also an mein Compiler hat den doch erkannt und umgebaut.
Warum sollte der den er selbst erzeugt auch diese Ausgabe machen???Das liegt daran, WIE der Compiler den Default-Assign zusammenbaut: Er ruft erst den operator= für die Basisklasse auf und weist dann (mit deren operator=) alle nichtstatischen Member zu, die die Klasse selber angelegt hat.
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

Was bedeutet die Eigenschaft CopyConstructible zu haben.
Ganz einfach: Der Typ muß einen öffentlich zugänglichen Kopier-Konstruktor besitzen.
Recht hast Du ich will eigentlich nur das ein double Vector auch mit float oder int Werte initialisiert werden kann. An dieser Stelle würde nach deiner Methode der Compiler implizite casts einbauen die das machen was ich will nur schöner???
Was hast du gegen implizite Casts? (btw, erstaunlich ist, daß du beim NumericVector genau diese Methode verwendet hast)
Mit *= verändere ich doch mein aktuelles Objekt, was ich doch im Allgemeinen garnicht tun möchte.
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; }camper schrieb:
auf das problem mit dem vererbten operator* gehe ich hier nicht ein, denn ich habe starke zweifel, dass vererbung hier überhaupt genutzt werden sollte. auf jeden fall benötigst du dann einen konvertierungs-ctor, der einen Vector<T> in einen NumericVector<T> konvertiert - und dann würde es auch mit deinem code funktionieren (ob es das ist, was du willst, ist eine andere frage).
Ja ich glaube das will ich im Moment.
Wenn du jeden Vector als NumericVector verwenden willst, wieso definierst du dann überhaupt zwei verschiedene Typen?
-
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.
-
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...