Frage zur Typdefinition
-
Hallo Leute,
eigentlich eine allerweltsfrage frage, aber ich komm grade nicht richtig klar damit
Ich berechne in einem Programm Vektoren. Dafür nutze ich immer diese Art: std::array<double,3>
Das möchte ich gern in einen eigenen Typ packen, sodass die Funktionalität von std::array erhalten bleibt, also alle Funktionen davon weiterhin aufrufbar sind. Irgendwie komm ich aber nicht damit zurecht. Wie genau mach ich das? Reicht da sowas:
typedef std::array<double, 3> Vektor;
Oder muss ich das richtig als neue Klasse ableiten? Was passiert wenn ich Vektor an Funktionen übergebe? Wird das dann genauso wie bei std::array gehandhabt?
-
Ja, genau so gehts. Es gibt noch eine alternative Schreibweise:
using Vector = std::array<double, 3>;
In diesem Fall macht es keinen Unterschied, aber wenn du Funktionszeiger deklarierst ist die using Schreibweise einfacher und aus Konsistenzgründen würde ich immer using nehmen.
-
Kann ich so auch die Operatoren überladen? Im Sinne von:
using Vektor = std::array<double, 3>; Vektor operator+ (Vektor& leftVec, const Vektor& rightVec){ leftVec[0] = leftVec[0] + rightVec[0]; leftVec[1] = leftVec[1] + rightVec[1]; leftVec[2] = leftVec[2] + rightVec[2]; return leftVec; }
Oder muss ich um sowas zu tun docheine eigene Klasse anlegen? Und gilt der operator so wie oben dann eigentlich auch gleichzeitig für std::array? Das macht keinen Sinn oder?
-
Ja geht. Und auch für std::array<double, 3>.
-
Freddy_Kay schrieb:
Kann ich so auch die Operatoren überladen?
Ja, das ist möglich, aber nicht unbedingt ratsam.
Oder muss ich um sowas zu tun docheine eigene Klasse anlegen?
Dies wäre ratsam.
Und gilt der operator so wie oben dann eigentlich auch gleichzeitig für std::array?
Der gilt dann, in diesem Beispiel, für alle std::array<double, 3>. Denk dran: Ein Template (z.B. std::array) ist noch keine fertige Klasse. Erst eine Instanzierung (z.B. std::array<double, 3>) ist eine vollständige Klasse und ein eigenständiger Typ. Eine andere Instanzierung (z.B. std::array<double, 4>) wäre ein komplett anderer Typ.
Das macht keinen Sinn oder?
So ist es, denn du willst schließlich keine Addition von Vektoren mit Arrays. Daher ist es ratsam, das in einer eigenen Klasse zu wrappen. Hier bietet sich vielleicht mal private-Vererbung und ein paar usings an. Denn du willst eigentlich nicht alle Member von std::array in einem mathematischen Vektor haben, oder? Iteration über einen Vektor macht selten Sinn.
Andererseits brauchst du doch sogar so gut wie gar keine Member von std::array, oder? Welche denn, außer operator[]? Vielleicht ist es besser, das std::array als Member zu halten oder gar gänzlich auf das std::array zu verzichten und ein rohes Array als Member zu halten. So etwas kann man eigentlich recht flott selber schreiben. Hier eine kleine Vektorklasse. Nicht sonderlich durchdesigned, sondern bloß schnell runtergetippt, aber trotzdem gut genug, dass ich sie regelmäßig benutze:
namespace vector_h_implementation { template <typename T, unsigned R> class VectorBase { private: typedef VectorBase<T, R> Self; public: typedef T value_type; static unsigned rank() { return R; } const T& operator[](unsigned i) const { return data[i]; } T& operator[](unsigned i) { return data[i]; } bool operator==(const Self &v2) const { for (unsigned int i=0; i < R; i++) if ((*this)[i] != v2[i]) return false; return true; } bool operator!=(const Self &v2) const { return !(*this == v2); } template < class numtype > Self& operator*=(const numtype num) { for (unsigned int i=0; i < R; i++) (*this)[i] *= num; return *this; } template < class numtype > Self& operator/=(const numtype num) { for (unsigned int i=0; i < R; i++) (*this)[i] /= num; return *this; } Self& operator+=(const Self &v2) { for (unsigned int i=0; i < R; i++) (*this)[i] += v2[i]; return *this; } Self& operator-=(const Self &v2) { for (unsigned int i=0; i < R; i++) (*this)[i] -= v2[i]; return *this; } double get_sqr_length() const { double res = 0.0; for (unsigned int i=0; i < R; i++) res += (*this)[i] * (*this)[i]; return res; } double get_length() const { return std::sqrt(get_sqr_length()); } double sqr_length() const { return get_sqr_length(); } double length() const { return get_length(); } private: T data[R]; }; template< class value_t, unsigned int rank > std::ostream &operator<<(std::ostream& out, const VectorBase< value_t, rank >& v) { for (unsigned int i=0; i < rank-1; i++) out << v[i] << '\t'; out << v[rank-1]; return out; } template <typename T> class V2D: public VectorBase<T, 2> { private: typedef VectorBase<T,2> Base; public: V2D() {}; V2D(const T& v0, const T&v1) { (*this)[0] = v0; (*this)[1] = v1; } }; template <typename T> class V3D: public VectorBase<T, 3> { private: typedef VectorBase<T,2> Base; public: V3D() {}; V3D(const T& v0, const T& v1, const T& v2) { (*this)[0] = v0; (*this)[1] = v1; (*this)[2] = v2; } }; template<class Vector> Vector operator-(const Vector& v1, const Vector& v2) { Vector result; for (unsigned i = 0; i < Vector::rank(); ++i) result[i] = v1[i] - v2[i]; return result; } template<class Vector> Vector operator+(const Vector& v1, const Vector& v2) { Vector result; for (unsigned i = 0; i < Vector::rank(); ++i) result[i] = v1[i] + v2[i]; return result; } template<class Vector> double operator*(const Vector& v1, const Vector& v2) { double result = 0; for (unsigned i = 0; i < Vector::rank(); ++i) result += v1[i] * v2[i]; return result; } template<class Vector> Vector operator*(double d, const Vector& v2) { Vector result = v2; result *= d; return result; } template<class Vector> Vector operator*(const Vector& v1, double d) { Vector result = v1; result *= d; return result; } template<class Vector> Vector operator/(const Vector& v1, double d) { Vector result = v1; result /= d; return result; } } typedef vector_h_implementation::V2D<double> Vector2D; typedef vector_h_implementation::V2D<int> Int2D; typedef vector_h_implementation::V3D<double> Vector3D; typedef vector_h_implementation::V3D<int> Int3D;
Ich sehe auch prompt gerade ein paar Stellen, die man mit C++11 deutlich besser machen könnte (die Klasse ist schon etwas älter).
-
SeppJ schrieb:
So ist es, denn du willst schließlich keine Addition von Vektoren mit Arrays. Daher ist es ratsam, das in einer eigenen Klasse zu wrappen. Hier bietet sich vielleicht mal private-Vererbung und ein paar usings an. Denn du willst eigentlich nicht alle Member von std::array in einem mathematischen Vektor haben, oder? Iteration über einen Vektor macht selten Sinn.
Andererseits brauchst du doch sogar so gut wie gar keine Member von std::array, oder? Welche denn, außer operator[]? Vielleicht ist es besser, das std::array als Member zu halten oder gar gänzlich auf das std::array zu verzichten und ein rohes Array als Member zu halten. So etwas kann man eigentlich recht flott selber schreiben. Hier eine kleine Vektorklasse. Nicht sonderlich durchdesigned, sondern bloß schnell runtergetippt, aber trotzdem gut genug, dass ich sie regelmäßig benutze
Ich wollt mir das ganze nur ersparen und dachte es geht mit der verwendung vom std:: schneller und einfacher. In wahrheit hab ichs aber schin längere Zeit kommen sehen.
Nicht böse sein Sepp aber ich hab deinen Code schamlos geklaut. Und werd den für meine Zwecke anpassen. Verzichte bitte auf Copyright infringement
An alle: danke für eure Hilfe!
-
Freddy_Kay schrieb:
Nicht böse sein Sepp aber ich hab deinen Code schamlos geklaut. Und werd den für meine Zwecke anpassen. Verzichte bitte auf Copyright infringement
Kein Problem, ich habe ihn ja nicht gepostet, damit du ihn dir bloß angucken darfst . Die Rückgabewerte des Skalarprodukts und die Konstruktoren könnte man mittlerweile viel schöner machen, ein paar weitere Sachen auch. Ich gucke mir das später vielleicht mal an. (Andererseits habe ich auch nie Konstruktoren für > 3 Dimensionen gebraucht und auch kein verallgemeinertes Skalarprodukt, insofern wäre die Verschönerung reiner Selbstzweck.)