Suche ne Vector implementierung (nicht std::vector)
-
Richtig, aber
Vektorklassenfachmann schrieb:
Naja, darüber kann man auch wieder ein wenig streiten. Es gibt schon eine Reihe recht guten Argumenten, die dagegen sprechen:
1.) Die builtins müssen auch von Hand initialisiert werdenIst ein vector ein Builtin? - Nein. Ich als User erwarte, dass es anständig 0-Initialisiert ist. (Muss nicht sein. Gut dokumentiert spricht eigl. nix dagegen das nicht zu machen.)
Vektorklassenfachmann schrieb:
2.) Ein Vektor hat keinen eindeutigen Default-Wert (wobei ein Nullvektor natürlich naheliegend wäre)
Ich würde sagen, dass die Member von einem Vektor 0-initialisiert sind, dann ist auch der Vektor 0-initialisiert..
Vektorklassenfachmann schrieb:
3.) Es kostet Zeit und nützt nicht viel.
Da stimme ich dir nicht zu. Es kostet definitiv mehr Zeit,aber das ist im Normalfall nicht das Problem. Es nützt schon, da es Fehler zuvorkommen kann, wo man sich auf 0-Initialisierung verlässt.
Bei einer Matrix ist das z.B wieder eine andere Frage. Wenn z.B viele Matrizen ständig erzeugt und zerstört werde, kann es durchaus ein Flaschenhals werden. Bei einem Vektor ist das aber imo weniger der Fall. (Ich habe eine Sprite-Engine, die ständig neue Objekte erstellt, die Hauptsächlich Vektoren beinhaltet und die Erzeugung ist nich annährend im Optimierkritischen Bereich..;))
-
1.) Die builtins müssen auch von Hand initialisiert werden
EDIT:
Doch, jetzt habe ich den Satz verstanden. Meine Antwort war quatsch.2.) Ein Vektor hat keinen eindeutigen Default-Wert (wobei ein Nullvektor natürlich naheliegend wäre)
Zumindest sollte klar sein ob der Vector sinnige Werte enthält. Da ist alles besser als ein undefinierter Vektor (es seidenn, dieser hätte ein isValid Flag o.ä.)
3.) Es kostet Zeit und nützt nicht viel.
Was kostet Zeit? Das Coden oder die Konstruktion?
-
Meine Vorschläge:
1. Ich würde den Typ der Klasse zum Template machen
2. Ich würde die dimension als Template übergebentemplate<class T, int DIM> class Vector { T getVal(int dim); void setVal(int dim, T val); private: T m_vec[DIM]; }3. ich würde getter und setter schreiben (für spezialisierte 2d und 3d Templates).
da vekoren aber ein standardkonstrukt sind, würde ich die zugriffsfunktionen kurz halten:T x()const { return m_vec[0];} T y()const { return m_vec[1];} T z()const { return m_vec[2];} T& x() { return m_vec[0];} T& y() { return m_vec[1];} T& z() { return m_vec[2];}Das ist zwar nicht ganz so üblich, aber das Kombiniert die Vorteile von getter/Setter mit einfacher Benutzbarkeit.
man kann dann sowas schreiben.Point<float,3> p; p.x() += 3;
-
Da ich mit openscenegraph vertraut bin, nehme ich die Vektorklassen davon.
btw würde Typisierung sich nicht eher negativ auf die Performance auswirken?
-
Seikilos schrieb:
static double angleInBetweenDegree(const Vector & v1, const Vector &v2) { double angle = std::acos(v1.dotProduct(v2)); return std::min(angle, 360-angle); }eigentlich gilt doch:
φ = ZWischenwinkel
a = der eine Vektor
b = der andere Vektor
° = Ersatz für das Skalarproduktzeichen
cos φ = (a°b)/(|a|*|b|)
sprich bei deinem "angleInBetweenDegree" fehlt die Division durch das Produkt der 2 Vektorlängen
außerdem gibt std::acos doch rad-Angaben und keine Gradangaben (also Grad im Sinne von Degree) zurück oder?:xmas1:
-
drakon, wie oft benötigst du einen Nullvektor? Und wie oft kommst du in eine Situation, in der du z.B. viele Vektoren in einem std::vector "vorbereitest", um sie später mit anständigen Werten zu befüllen. Die Null-Initialisierung hat dir dann nichts gebracht.
Für mich erscheint ein Fall, in dem man wirklich Nullvektoren benötigt, ein wenig konstruiert. Auf jeden Fall sollten die seltener sein, als jene, in denen man Vektoren erzeugt aber erst später mit anständigen Werten belegt. Das kann man zwar bestimmt oftmals auch optimieren, aber das bleibt trotzdem meine Meinung.
Und der Vergleich zu Matrizen... Angenommen, ich habe dreimal so viele Vektoren, als Matrizen. Dann habe ich einen äquivalenten Flaschenhals, natürlich von 3x3 Matrizen ausgehend.
LordJaxom, wie sinnig ist ein Nullvektor? Man definiert damit Undefiniertheit, was für einen Vektor nicht wirklich viel Sinn macht. Bei Zeigern schreibe ich oft Code, der von der Gültigkeit des Zeigers abhängt. Wie oft mache ich das für Vektoren? Wenn ich einen Vektor verwende bevor ich ihn mit korrekten Werten belegt habe, führt es in beiden Fällen zu einem Fehler. Ein Nullvektor erleichtert das Debugging unwesentlich, in der Release-Version bietet er gar keine Vorteile mehr.
Ein isValid-Flag sollte eine Vektorklasse selbst nicht haben, höchstens eine Klasse die Vektoren hält und für ein solches Flag auch einen guten Grund hat (ein Vektorenmanager meinetwegen
... ich weiß im Moment keinen guten Anwendungsfall!)Zeit kostet es natürlich bei der Konstruktion.
-
wth, ich sagte doch bereits das er den Vektor als eigene Klasse initialisieren sollte, dann gibts kein typedef struct und keine public Variablen.
-
Zu den Boost-Operatoren: Ich finde sie an sich eine schöne Sache, aber Compile-Zeit (erhöht wegen boost/operators.hpp, wird wahrscheinlich in fast jedem Modul eingebunden) kann auch ein Kriterium bei der Code-Entwicklung sein. Die Redundanz oder Tippzeit sollte bei dieser Vector-Klasse imho letzt-rangig sein.
-
drakon, wie oft benötigst du einen Nullvektor? Und wie oft kommst du in eine Situation, in der du z.B. viele Vektoren in einem std::vector "vorbereitest", um sie später mit anständigen Werten zu befüllen. Die Null-Initialisierung hat dir dann nichts gebracht.
Für mich erscheint ein Fall, in dem man wirklich Nullvektoren benötigt, ein wenig konstruiert. Auf jeden Fall sollten die seltener sein, als jene, in denen man Vektoren erzeugt aber erst später mit anständigen Werten belegt. Das kann man zwar bestimmt oftmals auch optimieren, aber das bleibt trotzdem meine Meinung.
Hmm. Kommt auf den Programmierstil drauf an. Zugegeben früher hatte ich eher davon gebrauch gemacht, da ich eher "Move"-Funktionen gehabt habe. Sprich ich bin davaon ausgegangen, dass ich bei 0,0,0 bin und dann von dortaus mit += usw. gearbeitet. Da ist das dann schon recht Mühsam, wenn man immer dran denken muss, dass der Vektor sonst wo ist.
Aber es geht mir persönlich nichteinmal unbedingt um das, sonder auch um die persönliche Preferenz, dass ich einfach alles initialisiert haben will, wissend, was für einen Wert der hat. Ist mir persönlich mehr Wert, als eine mögliche Performance Verbesserung, die ich eh nicht brauche..Und der Vergleich zu Matrizen... Angenommen, ich habe dreimal so viele Vektoren, als Matrizen. Dann habe ich einen äquivalenten Flaschenhals, natürlich von 3x3 Matrizen ausgehend.
Das war eher auf darauf bezogen, dass Matrizen eher für andere Sachen benutzt werden, wo wirklich praktisch nur erzeugt/zerstört wird.. Das es einfach 3/4 mal so viel ist, ist klar, aber der Verwendungszweck ist ein wenig anderst.. (Und dort kann es imo auch wirklich was ausmachen..)
Zu den Boost-Operatoren: Ich finde sie an sich eine schöne Sache, aber Compile-Zeit (erhöht wegen boost/operators.hpp, wird wahrscheinlich in fast jedem Modul eingebunden) kann auch ein Kriterium bei der Code-Entwicklung sein. Die Redundanz oder Tippzeit sollte bei dieser Vector-Klasse imho letzt-rangig sein.
Ein/Der Grund, warum ich sie nicht nutze. Den Header will ich so einfach, wie möglich halten, da, wie du schon sagst viel Overhead mitkommt. (Obwohl es ja ansonsten noch PCH gibt)
-
Vektorklassenfachmann schrieb:
Zeit kostet es natürlich bei der Konstruktion.
Das ist meiner Meinung nach Unsinn. Aus meinen bescheidenen Erfahrungen mit Assembler weiss ich, dass man Speicher reservieren kann oder Speicher reservieren und im gleichen Schritt auch initialisieren. Mehrkosten entstehen da definitiv nicht. Und auch für den Fall, dass meine bescheidenen Kenntnisse da jetzt was falsches behaupten, es würde völlig unwesentlichen Mehraufwand benötigen. Die Flaschenhälse liegen wo anders!
Desweiteren kann man den Konstruktor ja auch auf diese Art und Weise erstellen:
Vector(Value const& x = Value(), Value const& y = Value(), Value const& z = Value()) : x(x) , y(y) , z(z) { }Unter der Annahme, dass Value irgendein typedef oder Templateparameter ist.
Grüssli
-
Das sagen dir deine Assembler erfahrungen, ja klar.
Speicher reservieren und initialisieren sind immer separate dinge.
reservieren ist im einfachsten Falle eine Decrementierung des stackpointers im komplizierteren ein Betreibsystemaufruf.
In beiden fällen steht in dem Speicher irgendwas drin, ws erst mal überschrieben werden muss.Ausnahme:
statischer speicher, der wird zur kompilezeit festgelegt, wenn ihc den im Code vorinitialisiere (nur mit nativen Datentypen, keine klassen) wird das so ins datensegment gepackt.
Bei klassen wird trotzdem zur Laufzeit der Konstruktor aufgerufen.
-
Wie schon mehrfach angedeutet wurde, lohnt sich diese Mikrooptimierung aber kaum. Dadurch läuft man nur Gefahr, dass einmal ein uninitialisierter Vektor verwendet wird.
In den anderen Fällen verliert man vielleicht einige Mikrosekunden. Darüber kann man aber nachdenken, wenn alle grösseren Performanceprobleme behoben sind und das Programm wirklich derart zeitkritisch sein muss.
-
vlad_tepesch schrieb:
Das sagen dir deine Assembler erfahrungen, ja klar.
Ah, weisst du was, ich hab das mit den Registern verwechselt. Ich sagte ja bescheiden
(irgendwann muss ich die aber wirklich noch verbessern)
Aber es ist trotzdem egal, deshalb hatte ich noch einen zweiten Satz hingeschrieben. Da gehen maximal ein paar Taktzyklen drauf und man sollte ja wirklich nicht schon beim Programmieren darauf achten, dass man wenig Taktzyklen verwendet ^^Grüssli