besserer Code-Stil für arrays
-
Am besten eine kleine Wrapper-Klasse schreiben:
class Matrix { public: Matrix(size_t m, size_t n) : _mat(m * n), _m(m), _n(n) { } int &operator()(size_t i, size_t j) { return _mat[i * _n + j]; } const int &operator()(size_t i, size_t j) const { return _mat[i * _n + j]; } size_t m() const { return _m; } size_t n() const { return _n; } private: std::vector<int> _mat; size_t _m; size_t _n; }; int main() { Matrix m(3, 2); m(0, 1) = 3; m(2, 0) = 9; for ( size_t i = 0; i < m.m(); ++i ) { for ( size_t j = 0; j < m.n(); ++j ) std::cout << m(i, j) << ' '; std::cout << '\n'; } }
Natuerlich schreibt man am besten gleich eine Template-Klasse.
Grundsaetzlich ist der Zugriff auf
std::vector<int> v(m * n);
effizienter als beistd::vector<int> v(m, std::vector<int>(n));
. Ersteres ist aber muehsam, da man den Index immer manuell berechnen muss. Deshalb eine kleine Wrapper-Klasse.
-
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?
wob schrieb:
Und zur 2. Frage (Übergabe an Funktion): warum nicht, wie in der STL üblich, einfach ein entsprechendes Iteratorpaar?
Eine Funktion einer Klasse wird oft aufgerufen. Die Variable (mat) dient nur als interner Zwischenspeicher der Klasse. Von außerhalb weiß man nicht unbedingt wo der Zugriff ist. Die Klasse hat auch mehrere davon. Man müsste dann 10 iteratoren übergeben.
icarus2 schrieb:
Grundsaetzlich ist der Zugriff auf
std::vector<int> v(m * n);
effizienter als beistd::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.
-
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 beistd::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.
-
icarus2 schrieb:
Am besten eine kleine Wrapper-Klasse schreiben:
Oder gleich fertige Produkte verwenden, wie etwa Boost.MultiArray
-
**Styler schrieb:
icarus2 schrieb:
Grundsaetzlich ist der Zugriff auf
std::vector<int> v(m * n);
effizienter als beistd::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?
-
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.
-
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 multiplizierenfor (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]
-
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.