Prosty widok na macierz 2D w C++

Dziś tak bardziej o C++. Dość często widzę kod mający w zamierzeniu obsługiwać macierze wyglądający mniej więcej tak:

int** matrix = new int*[h];
for(int i = 0; i < h; ++i) {
    matrix[i] = new int[w];
}

Pomijając nawet kwestię wydajności (to taka macierz JP na 100%, gdzie P to Prefetch), jest to okropny kod. Korzystanie z nagiego new i delete to antyidiom w C++ (szczególnie delete, szerzej opisałem to n.p. tutaj i tutaj). Jest to:

  • niewygodne: kłopotliwa inicjalizacja, kolejność rząd/kolumna wymaga sprawdzenia sposobu inicjalizacji,
  • niebezpieczne: brak RAII, trzeba pamiętać o odpowiednim zwolnieniu zasobów, można niechcący nadpisać wskaźnik jednego rzędu innym rzędem,
  • niezgrabne: nie ma żadnego mechanizmu upewniającego, że wszystkie wiersze (lub kolumny) macierzy będą tej samej długości.

Niektórzy sugerują użycie vector<vector<T>>, ale to tak naprawdę rozwiązuje wyłącznie drugi z wyżej wymienionych problemów (będąc jeszcze gorszym dla cachevector to de facto trzy wskaźniki). Faktycznie, może być on uznany za najbardziej istotny, ale korzystnym by było również rozwiązanie pozostałych. W przypadku gdy znamy jeden lub dwa wymiary macierzy w czasie kompilacji możemy użyć vector<array<T, N>> lub array<array<T, N>, M>. Jeśli jednak tak nie jest, cały czas istnieje możliwość użycia ciągłego bufora (przyjaznego dla cache) i prostego widoku na dane jako na macierz 2D. Przykładowo:

vector<double> data{1,2,3,4,5,6,7,8,9,10,11,12};
 
simple_2d_matrix_view<double> m(data.data(), 2, 6);
 
for(size_t i = 0; i < m.height(); ++i) {
	for(size_t j = 0; j < m.width(); ++j) {
		cout << m(i, j) << " ";
	}
	cout << "\n";
}

Implementacja użytego wyżej szablonu klasy simple_2d_matrix_view może wyglądać tak:

template<typename T>
class simple_2d_matrix_view
{
	T* data_;
	size_t width_;
	size_t height_;
 
public:
 
	simple_2d_matrix_view(T* ptr, size_t h, size_t w):
		data_{ptr},
		width_{w},
		height_{h}
	{}
 
	size_t width() const { return width_; }
	size_t height() const { return height_; }
 
	T& operator()(size_t h, size_t w) {
		assert(w < width_);
		assert(h < height_);
		return data_[width_ * h + w];
	}
 
	T const& operator()(size_t h, size_t w) const {
		return const_cast<simple_2d_matrix_view&>(*this)(h, w);
	}
};

One thought on “Prosty widok na macierz 2D w C++

  1. Bardzo fajny bajer. Ja do tej pory używałem aliasu using Mtrx = vector< i jakoś żyłem xD.
    A co sądzisz o boost::multiarray?

Leave a Reply

Your email address will not be published.