besserer Code-Stil für arrays



  • Bei nem array fester Größe kann man wie gesagt std::array nehmen, ein kleines Anwendungsbeispiel sähe dann so aus

    #include <iostream>
    #include <array>
    
    template<std::size_t HEIGHT, std::size_t WIDTH>
    void f(std::array<std::array<int, WIDTH>, HEIGHT>& a)
    {
    	for(std::size_t i = 0; i < HEIGHT; ++i)
    	{
    		for(std::size_t j = 0; j < WIDTH; ++j)
    			a[i][j] = i;
    	}
    }
    
    int main()
    {
    	const int WIDTH = 4, HEIGHT = 3;
        std::array<std::array<int, WIDTH>, HEIGHT> array;
    	f(array);
    	for(auto iter = array.begin(); iter != array.end(); ++iter)
    	{
    		for(auto innerIter = iter->begin(); innerIter != iter->end(); ++innerIter)
    			std::cout << *innerIter << " ";
    		std::cout << "\n";
    	}
    }
    

    **Styler schrieb:

    Incocnito schrieb:

    Mit rohen Zeigern rumhantieren ist aufgrund der schlechteren Handhabung und des Risikos von Speicherlecks bei manueller Speicherverwaltung eine schlechte Idee.

    Und macht das effizienztechnisch keinen Unterschied?

    icarus2 schrieb:

    Grundsaetzlich ist der Zugriff auf std::vector<int> v(m * n); effizienter als bei std::vector<int> v(m, std::vector<int>(n)); . Ersteres ist aber muehsam, da man den Index immer manuell berechnen muss.

    Sicher? Man muss doch dann immer eine Multiplikation ausführen. v[m] wäre nur einmal lesen.

    In Zeiten von 2 Ghz Prozessoren und 1,3 Ghz RAM braucht man sich wohl kaum darum Gedanken machen, ob der vector jetzt ein paar Mikrosekunden langsamer ist. Klar hast du durch das Drumherum ein bisschen mehr Overhead, aber bekommst halt die ganze Palette an STL-Annehmlichkeiten, die den Performanceverlust meistens mehr als wettmachen. Und beim std::array hast du besagte Annehmlichkeiten und praktisch keinen Overhead, da std::array per Definition nur ein ganz dünner Wrapper um einen C-Array ist.


  • Mod

    icarus2 schrieb:

    Am besten eine kleine Wrapper-Klasse schreiben:

    Oder gleich fertige Produkte verwenden, wie etwa Boost.MultiArray


  • Mod

    **Styler schrieb:

    icarus2 schrieb:

    Grundsaetzlich ist der Zugriff auf std::vector<int> v(m * n); effizienter als bei std::vector<int> v(m, std::vector<int>(n)); . Ersteres ist aber muehsam, da man den Index immer manuell berechnen muss.

    Sicher? Man muss doch dann immer eine Multiplikation ausführen. v[m] wäre nur einmal lesen.

    Nichts hindert dich, einmalig

    auto p = &v(m,0);
    

    zu ermitteln. Von immer Multiplizieren müssen kann keine Rede sein. Das ist nur dann erforderlich, wenn Zugriffe zufällig erfolgen. Im analogen Fall sind das immer zwei Speicherzugriffe mit dem anderen Speichermodell.



  • camper schrieb:

    Nichts hindert dich, einmalig

    auto p = &v(m,0);
    

    zu ermitteln.

    Doch mein Compiler hindert mich 🙂

    int m=10, n=10;
     //std::vector<int> v(m, std::vector<int>(n)) sollte denke ich so sein:
     std::vector<std::vector<int>> v(m, std::vector<int>(n))
     auto p = &v(m,0); //-> error: no match for call to ‘(std::vector<std::vector<int> >) (int, int)’
     auto p = &v[m,0]; // das geht aber
    
    /////////////////////////////
      std::vector<int> v(m * n);
      int i=0;
      for(std::vector<int>::iterator g = v.begin(); g != v.end(); ++g)   
         *g=i++;
      auto p = &v[m,0]; //*p ist 0
      auto p = &v[m,1]; //*p ist 1
      auto p = &v[m,2]; //*p ist 2
      auto p = &v[42,2]; //*p ist 2
    

    Was hat die erste Stelle zu bedeuten?


  • Mod

    noStyler schrieb:

    camper schrieb:

    Nichts hindert dich, einmalig

    auto p = &v(m,0);
    

    zu ermitteln.

    Doch mein Compiler hindert mich 🙂

    std::vector<std::vector<int>> v(m, std::vector<int>(n))
     auto p = &v(m,0); //-> error: no match for call to ‘(std::vector<std::vector<int> >) (int, int)’
    

    Von dieser Kombination war offenkundig nicht die Rede.



  • Tut mir Leid, von welcher dann?

    std::vector<int> v(m * n)
    auto p = &v(m,0);
    

    hatte ich auch probiert und ging auch nicht.


  • Mod

    noStyler schrieb:

    Tut mir Leid, von welcher dann?

    std::vector<int> v(m * n)
    auto p = &v(m,0);
    

    hatte ich auch probiert und ging auch nicht.

    Für einen std::vector<int> v(m * n); erfolgt der Zugriff auf ein Element (x,y) durch

    v[x*n+y]
    

    bzw.

    m(x,y)
    

    mit icarus2s Matrixklasse.
    Will man nun z.B. über die Elemente einer Zeile x iterieren, ist es nicht erforderlich, ständig zu multiplizieren

    for (int y = 0; y < n; ++y)
        v[x*n+y]
    

    sondern:

    auto p = &v[x*n]; // &m(x,0) mit Matrixklasse
    for (int y = 0; y < n; ++y)
        p[y]
    

  • Mod

    Oder noch eleganter, da es zu dem üblichen Vorgehen bei Containern passt und der Benutzer daher völlig agnostisch bezüglich der inneren Struktur sein darf: Biete das was camper gezeigt hat als Iterator an.



  • Danke für die Antworten.
    Habe es vielleicht nicht richtig geschrieben.

    camper schrieb:

    auto p = &v[x*n]; // &m(x,0) mit Matrixklasse
    for (int y = 0; y < n; ++y)
        p[y]
    

    dass würde dann wiederum sehr oft aufgerufen und da meinte ich müsste dann oft, d.h. bei jedem Aufruf neu multipliziert werden.Wäre dann dann nicht 2D besser (v vektor von vektoren)? also:

    auto p = &v[x];
    


  • Wenn du Matrizen mit fester Größe hast, dh, std::array verwendest, formt der Compiler ganz sicher die Multiplikationen zu simplen shiftoperationen um.


Anmelden zum Antworten