If you want to create an array in which data is contiguous, and you do not need a one-dimensional array (that is, you want to use the syntax [][]
), then the following should work. It creates an array of pointers, and each pointer points to a position in the memory pool.
#include <iostream>
Please note that only 2 selections have been made. This is not only more efficient due to the smaller number of allocated allocations, but now we have more chances to roll back the allocated memory in the event of a memory allocation failure, in contrast to the βtraditionalβ way of allocating a two-dimensional array into non-contiguous memory:
// The "traditional" non-contiguous allocation of a 2D array (assume N x M) T** ptr; ptr = new T*[N]; for (int i = 0; i < N; ++i) ptr[i] = new T [M]; // <<-- What happens if new[] throws at some iteration?
If new[]
throws an exception somewhere while the for loop is running, you must roll back all successful calls to new[]
that occurred earlier β this requires more code and adds complexity.
Also pay attention to how you free up memory in the adjacent version - just two calls to delete[]
when distributed sequentially instead of a loop calling delete[]
for each row.
You can improve the design by making it a true class, instead of highlighting / freeing it as two separate functions.
Edit: the class is not RAII-like, as the comment says. I leave this as an exercise for the reader. In the code above, one thing is missing - checking that nRows and nCols> 0 when creating such an array.
Edit 2: try-catch
added to ensure proper rollback of memory allocation if std::bad_alloc
exception occurs when allocating memory.
Change: Example code with a three-dimensional array, similar to the one above , see this answer. Code is included to roll back resource allocation in case of distribution failure.
Change: added elementary class RAII:
template <typename T> class Array2D { T** data_ptr; unsigned m_rows; unsigned m_cols; T** create2DArray(unsigned nrows, unsigned ncols, const T& val = T()) { T** ptr = nullptr; T* pool = nullptr; try { ptr = new T*[nrows]; // allocate pointers (can throw here) pool = new T[nrows*ncols]{ val }; // allocate pool (can throw here) // now point the row pointers to the appropriate positions in // the memory pool for (unsigned i = 0; i < nrows; ++i, pool += ncols) ptr[i] = pool; // Done. return ptr; } catch (std::bad_alloc& ex) { delete[] ptr; // either this is nullptr or it was allocated throw ex; // memory allocation error } } public: typedef T value_type; T** data() { return data_ptr; } unsigned get_rows() const { return m_rows; } unsigned get_cols() const { return m_cols; } Array2D() : data_ptr(nullptr), m_rows(0), m_cols(0) {} Array2D(unsigned rows, unsigned cols, const T& val = T()) { if (rows == 0) throw std::invalid_argument("number of rows is 0"); if (cols == 0) throw std::invalid_argument("number of columns is 0"); data_ptr = create2DArray(rows, cols, val); m_rows = rows; m_cols = cols; } ~Array2D() { if (data_ptr) { delete[] data_ptr[0]; // remove the pool delete[] data_ptr; // remove the pointers } } Array2D(const Array2D& rhs) : m_rows(rhs.m_rows), m_cols(rhs.m_cols) { data_ptr = create2DArray(m_rows, m_cols); std::copy(&rhs.data_ptr[0][0], &rhs.data_ptr[m_rows-1][m_cols], &data_ptr[0][0]); } Array2D(Array2D&& rhs) noexcept { data_ptr = rhs.data_ptr; m_rows = rhs.m_rows; m_cols = rhs.m_cols; rhs.data_ptr = nullptr; } Array2D& operator=(Array2D&& rhs) noexcept { if (&rhs != this) { swap(rhs, *this); rhs.data_ptr = nullptr; } return *this; } void swap(Array2D& left, Array2D& right) { std::swap(left.data_ptr, right.data_ptr); std::swap(left.m_cols, right.m_cols); std::swap(left.m_rows, right.m_rows); } Array2D& operator = (const Array2D& rhs) { if (&rhs != this) { Array2D temp(rhs); swap(*this, temp); } return *this; } T* operator[](unsigned row) { return data_ptr[row]; } const T* operator[](unsigned row) const { return data_ptr[row]; } void create(unsigned rows, unsigned cols, const T& val = T()) { *this = Array2D(rows, cols, val); } }; int main() { try { Array2D<double> dPtr(10, 10); std::cout << dPtr[0][0] << " " << a2[0][0] << "\n"; } catch (std::exception& ex) { std::cout << ex.what(); } }