Daten in einer Matrix halten (C++)



  • Hallo alle miteinander,

    ich schreibe gerade an einer Matrixklasse (jaja, ich weiß das macht jeder irgendwann ;)). Ich überlege nur wie ich am besten die Daten intern halte. Ein vector der die ganze Matrix enthält, ein vector der die Zeilen-vectors enthält oder komplett auf die STL verzichten und selbst mit dynamischem Speicher arbeiten...?

    Danke für konstruktive Anregungen 🙂



  • Ich glaub ich werd mit dynamischem Speicher arbeiten indem ich mir eine Allocatorklasse schreibe. Ist das sinnvoll oder nicht. Ich will das ganze nämlich ziemlich schnell haben.



  • Oder ist das zu unsicher? Wie sieht es denn aus wenn ich vector nehme?

    Welchen Speichertyp empfiehlt ihr?

    1. double**
    2. vector<double>
    3. vector<vector<double>>

    Mfg,

    Fredo (der langsam Selbstgepräche in diesem Thread führt 😉 )



  • Fredo schrieb:

    Oder ist das zu unsicher? Wie sieht es denn aus wenn ich vector nehme?
    Welchen Speichertyp empfiehlt ihr?

    1. double**
    2. vector<double>
    3. vector<vector<double>>
      Mfg,
      Fredo (der langsam Selbstgepräche in diesem Thread führt 😉 )

    weder noch.
    also double** fällt völlig aus, weil du ja dabei andauernd über die grenzen läufst, nen destruktor, copy-ctor und nen op= brauchst.
    vector<double> würde gehen.
    vector<vector<double>> liegt nicht in deinem sinne, denn jeder einzelne der untervektoren braucht nicht ne eigene größe zu haben.

    ich nehme mal an, du baust ne klasse Matrix, die so ne matrix wie in mathe ist, wo man auch zwei matritzen plutimizieren kann und so. die sollte als (einziges?) member wohl ein Array2D<double> haben. also speicherdinge und arrayzugriff auf ne hilfsklasse verschieben und in Matrix nur feine mathesachen machen. oh, die Array2D<double> könnte sich allein um den zugriff kümmern und die speichersachen von ner Array<double> machen lassen.
    aber wie kömmt der böse onkel volkard wiedermal drauf, sich ne Array<double> selber zu basteln, statt vector<double> zu verwenden? liegt wohl daran, daß er so unwahrscheinlich gerne mit assert oder ähnlichem immer und andauernd auf indexgrenzenüberschreitungen prüfen will und hier auch nicht capacity und size trennen würde.



  • volkard schrieb:

    aber wie kömmt der böse onkel volkard wiedermal drauf, sich ne Array<double> selber zu basteln, statt vector<double> zu verwenden? liegt wohl daran, daß er so unwahrscheinlich gerne mit assert oder ähnlichem immer und andauernd auf indexgrenzenüberschreitungen prüfen will und hier auch nicht capacity und size trennen würde.

    Hihi, danke Onkel Volkard für deine Tipps. Werde es mal so wie du gesagt hast bauen.



  • Wenn die Größe der Matrizen zur Kompilierungszeit feststeht, kannst du template-Parameter draus machen und ein statisches Array als Element nehmen. Das dürfte wohl das schnellstmögliche sein:

    template<unsigned W, unsigned H>
    class Matrix
    {
        double data[W][H];
    public:
        ...
    };
    


  • @Volkard
    wieso würdest du dringend von einem 2dimensionalen double attay abraten? man kann auf bereichsüberschreitungen doch auch "manuell" überprüfen, indem man der matrix zu begin (kionstruktor) eine feste dimension NxM gibt, und bei löschen/addieren von zeilen/spalten enstprechend ändert. aber ein gekapseltes array macht vielleicht mehr sinn, wenn man mehr exportieren will (z.b. zeilen oder spalten aus einer matrix), und nicht nur +,-,* etc rechnen will, ich weis ja nicht genau, füpr was es denn gebraucht wird

    noch als tip: wenn du dem größenwahn nicht abgeneigt, und gern mit kanonen auf spatzen schiest: mach doch die matrixklasse als template klasse, dann kannst duz sie gleichzeitig mit integern, dozublewerten, oder gar mit nem eigenen zahlentyp verwenden 🙂



  • Korbinian schrieb:

    noch als tip: wenn du dem größenwahn nicht abgeneigt, und gern mit kanonen auf spatzen schiest: mach doch die matrixklasse als template klasse, dann kannst duz sie gleichzeitig mit integern, dozublewerten, oder gar mit nem eigenen zahlentyp verwenden 🙂

    Was soll das denn heißen? Meine Matrix-Klasse arbeitet sinnvollerweise auch mit Templates. Ist doch mittlerweile das normalste der Welt solch eine Klasse durch Templates flexibler zu machen 😉 .



  • Korbinian schrieb:

    @Volkard
    wieso würdest du dringend von einem 2dimensionalen double attay abraten? man kann auf bereichsüberschreitungen doch auch "manuell" überprüfen, indem man der matrix zu begin (kionstruktor) eine feste dimension NxM gibt, und bei löschen/addieren von zeilen/spalten enstprechend ändert.

    wegen das hier:

    void machDichLeer()
    {
       for(int x=0;x<=m_maxx;++x)
          for(int y=0;y<=m_maxy;++y)
             data[y][x]=0;
    }
    

    uus, mist, hab ich doch <= statt < geschrieben. und mein double** data gebt mir keine debughilfe. aber kein problem, ich hab ja nen op[][] und schreib also:

    void machDichLeer()
    {
       for(int x=0;x<=m_maxx;++x)
          for(int y=0;y<=m_maxy;++y)
             (*this)[y][x]=0;//juhu, hier fliegt ne assertion
    }
    

    gefällt mit nicht wirklich. offensichtliches problemchen ist, daß ich mich zwinge, ungewöhnliche sachen zu tippen. wäre mein data gleich ne instanz einer klasse, die bereichsüberschreitungen testet, könnte ich gar nicht mehr ungetestete fehlerchen bauen.
    und das nichtoffensichtliche problemchen ist sogar das für mich schlimmere. die matrixklasse macht zu viele sachen. ich will "ein zweck, also eine klasse". destawegen hab ich ja sogar die Array2D und die Array getrennt, die Array2D macht nur bereichsüberprüfungen und bietet den op[][] an. die Array (evtl besser RawArray) macht nur speicherverwaltung. wenn die klassen so viel auf einmal können müssen, kriegen die immer ruck zuck 10 oder noch mehr methoden und dann verliere ich immer die übersicht, wenn ich sie ein paar monate nicht angeguckt habe. und dann denk ich "sollte ich kommentare reinschreiben?". und dann denke ich "nee, wenn daran bedarf ist, ist der code kacke".



  • operator void schrieb:

    Wenn die Größe der Matrizen zur Kompilierungszeit feststeht, kannst du template-Parameter draus machen und ein statisches Array als Element nehmen. Das dürfte wohl das schnellstmögliche sein

    Wenn das mal keine Idee ist 😉 . Du vergisst aber, dass man mit Matrizen in der Regel auch rechnen will (sonst könnte man ja auch sofort ein 2d-Array nehmen). Und genau da bekommst du ein Problem mit den Template-Parametern.



  • MaSTaH schrieb:

    operator void schrieb:

    Wenn die Größe der Matrizen zur Kompilierungszeit feststeht, kannst du template-Parameter draus machen und ein statisches Array als Element nehmen. Das dürfte wohl das schnellstmögliche sein

    Wenn das mal keine Idee ist 😉 . Du vergisst aber, dass man mit Matrizen in der Regel auch rechnen will (sonst könnte man ja auch sofort ein 2d-Array nehmen). Und genau da bekommst du ein Problem mit den Template-Parametern.

    warum?

    template<class T,size_t AM,size_t AN,size_t BM,size_t BN>
    Matrix<T,AM,BN> operator*(Matrix<T,AM,AN> const& a,Matrix<T,BM,BN> const& b);
    

    vielleicht freakiger mit sowaswas wie:

    template<class AT,size_t AM,size_t AN,class BT,size_t BM,size_t BN>
    Matrix<SumType<ProductType<AT,BT>::Type,ProductType<AT,BT>::Type>::Type,AM,BN> operator*(Matrix<AT,AM,AN> const& a,Matrix<BT,BM,BN> const& b);
    


  • Naja, aber ob dies das Wahre ist? 😉 Wenn du wirklich weißt wie groß die Matrizen sind dann geht sowas natürlich, aber das muss doch ein riesen Kompilat geben wenn man nur ein paar Rechenopertionen á la: "(A.transp() * 😎 * C". Unter Umständen hast du nach dieser Operation schon 4 Klassen generiert.



  • volkard schrieb:

    vector<vector<double>> liegt nicht in deinem sinne, denn jeder einzelne der untervektoren braucht nicht ne eigene größe zu haben.

    Dafür spräche aber, dass man so den mysteriösen [][]-"Operator" hätte. Andererseits könnte man ja auch () überladen. Sieht aber imho nicht wirklich nach Matrix aus wenn man sagt matrix(10, 2). Da finde ich matrix[10][2] schöner.



  • MaSTaH schrieb:

    Naja, aber ob dies das Wahre ist? 😉 Wenn du wirklich weißt wie groß die Matrizen sind dann geht sowas natürlich, aber das muss doch ein riesen Kompilat geben wenn man nur ein paar Rechenopertionen á la: "(A.transp() * 😎 * C". Unter Umständen hast du nach dieser Operation schon 4 Klassen generiert.

    das wahre zu suchen ist doch unser job.

    sagen wir mal, wenn der grafikprogger lauter 3- und 4-dimensionale hat, dann nimmt er wohl die einfachsten template-dinge. vor allem, weil der optimierer kleine schleifen aufrollen kann und so.

    wenn zur compilezeit die dimensionen bekannt sind, dann müssen die auch als template-parameter verhackstückelt werden. so hab ich wenigstens meyers verstanden. ein hoch auf die compilerfehler. und nieder mit den laufzeitfehlern. unser besonderer gruß geht hierbei natürlich an die java-fraktion.

    aber es tut ja keiner sagen, daß die ganzen template-instanzen code erzeugen müssen. wie wär's damit, daß sie nur leere wrapper um die ganz normale eine und einzige matrixklasse mit erst zur leufzeit bekannter größe sind? dann hättn wir, solange größen zur compilezeit bekannt sind, auch größenprüfungen zur compilezeit (und erst gar kein .invert() auf nichtquadratischen matrizen, statt ner exception (gruß)).
    es werden übrigens viel zu selten expression templates verwendet. die drängen sich mir hier wiedermal furchtbar auf.



  • MaSTaH schrieb:

    volkard schrieb:

    vector<vector<double>> liegt nicht in deinem sinne, denn jeder einzelne der untervektoren braucht nicht ne eigene größe zu haben.

    Dafür spräche aber, dass man so den mysteriösen [][]-"Operator" hätte. Andererseits könnte man ja auch () überladen. Sieht aber imho nicht wirklich nach Matrix aus wenn man sagt matrix(10, 2). Da finde ich matrix[10][2] schöner.

    das geheimnis um den myteriösen op[][] sollte inzwischen bereits der allgemeinheit bekanntgegeben worden sein. falls nicht, dann veröffentliche ich es hiermit:

    class Vector2D
    {//ungetestet
    private:
       Vector data;//der macht copy-ctor, dtor und op=
    public:
       class Zeilenproxy
       {
          private:
             Vector2D& v2d;
             int zeile;
          public:
             Zeilenproxy(Vector2D& _v2d,int _y)
             :v2d(_v2d),y(_y)
             {
             }
             double& operator[](int x)
             {
                return v2d.data[y*SIZEX+x];//kacke, nur demo
             }
       }
       ZeilenProxy operator[](int y)
       {
          return Zeilenproxy(*this,y);
       }
    };
    

    naheliegende verbesserungen:
    - const einbasteln
    - allein den proxy-trick zur aufgabe von Array2D machen. die implemetierung als template-parameter, die nen op()(int,int) hat.

    gibts eigentlich irgend nen brühmten, der immer sagt "eine klasse soll nur einen zweck haben", damit ich einen zum zitieren hätte?



  • volkard schrieb:

    das geheimnis um den myteriösen op[][] sollte inzwischen bereits der allgemeinheit bekanntgegeben worden sein. falls nicht, dann veröffentliche ich es hiermit:

    Also die Möglichkeit wäre mir nicht im Traum eingefallen. Thx für die Erleuchtung 👍 .



  • gibts eigentlich irgend nen brühmten, der immer sagt "eine klasse soll nur einen zweck haben"

    Einen? Es gibt wohl eher viele.
    Prinzipiell ist das ja nicht neu. Bereits bei Funktionen heitß die beste Kohäsion "functional cohesion", was soviel bedeutet wie: "performs one and only one operation".

    In der OOP gibt es sogar extra ein Prinzip dafür. Nennt sich Single Responsibility Principle (SRP) - "a class should have only one reason to change" (Robert C. Martin hat dazu ein schönes Kapitel geschrieben, dass man auf http://www.objectmentor.com/home findet)

    Wenn du explizit einen berühmten C++ Guru haben willst, kannst du z.B. Sutter zitieren, der in "Exceptional C++" schrieb:
    "Prefer cohesion. Always endeavor to give each piece of code- each module, each class, each function - a single, well-defined responsibility".



  • Könnt mir gar keine Klasse vorstellen die mehr als eine Aufgabe hat.

    Kennt jemand ein Beispiel?



  • Achso, in dem Paper sind Beispiele. Sorry. 😮


Anmelden zum Antworten