peaceman
peaceman

Reputation: 1529

dynamic allocating memory for 3d array that uses minimum memory and in contiguous form in c++?

I want to build a 3D nx*ny*nz matrix that nx,ny,nz are about 200 so I have to use dynamic allocation and because I have a lot of these matrixes I need build these matrixes in a way that it uses minimum memory as is possible how can I do this and how can I build in contiguous form?

Upvotes: 1

Views: 401

Answers (3)

bames53
bames53

Reputation: 88155

Using C++11:

template<typename T,size_t M,size_t N,size_t O>
using Matrix3D = std::array<std::array<std::array<T,O>,N>,M>;

std::unique_ptr<Matrix3D<double,200,200,200>> mat(new Matrix3D<double,200,200,200>);

(*mat)[m][n][o] = 10.0;

If you write a make_unique function the variable declaration becomes:

auto mat = std::make_unique<Matrix3D<double,200,200,200>>();

So a whole program might look like:

#include <memory> // std::unique_ptr for convenient and exception safe dynamic-memory management
#include <array>  // std::array because it behaves much better than raw arrays 

template<typename T,size_t M,size_t N,size_t O>
using Matrix3D = std::array<std::array<std::array<T,O>,N>,M>;

// A simple `make_unique`
template<typename T,typename... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

int main() {
    auto mat = make_unique<Matrix3D<double,200,27,200>>();
    (*mat)[199][26][199] = 10.0; // store 10.0 in the last element
}

Remember that this is C++11, and some compilers haven't yet implemented all the features I'm using. In particular the only released compiler I know of that supports template aliases (the using Matrix3D = line) is clang 3.0. The next version of GCC will support it though. Variadic templates (used to implement make_unique) are supported in both GCC and Clang, but not MSVC as of VS11.

Here's a version that uses only widely supported* C++11 features:

#include <memory>
#include <array>

template<typename T,size_t M,size_t N,size_t O>
struct Matrix3D {
    std::array<std::array<std::array<T,O>,N>,M> array;
};

// A simple `make_unique` that supports only zero-argument construction.
template<typename T>
std::unique_ptr<T> make_unique() {
    return std::unique_ptr<T>(new T);
}

int main() {
    auto mat = make_unique<Matrix3D<double,200,27,200>>();
    mat->array[199][26][199] = 10.0; // store 10.0 in the last element
}

* Widely supported means at least the latest releases of GCC, and MSVC.

Upvotes: 2

Emile Cormier
Emile Cormier

Reputation: 29209

You can write a wrapper around a std::vector and overload operator() to access matrix elements. The elements are stored contiguously in the 1D std::vector and operator() converts 3D indices into a 1D index into the std::vector. If the matrix was 2D, this is how the mapping from 2D to 1D would look like:

| 1 2 3 |
| 4 5 6 |  ---> [1 2 3 4 5 6 7 8 9] 
| 7 8 9 |

This ordering is called row major.

Here's an example of a class that overloads operator() to convert 3D indices into a row-major 1D index:

#include <iostream>
#include <vector>

template <class T>
class Matrix3D
{
public:
    Matrix3D(size_t m, size_t n, size_t o)
    : m_(m), n_(n), o_(o), data_(m*n*o) {}

    T& operator()(size_t i, size_t j, size_t k)
    {
        return data_[(i * n_ * o_) + (j * o_) + k];
    }

    const T& operator()(size_t i, size_t j, size_t k) const
    {
        return data_[(i * n_ * o_) + (j * o_) + k];
    }

private:
    std::vector<T> data_;
    size_t m_, n_, o_;
};

int main()
{
    Matrix3D<float> m(4, 3, 2);
    m(0,0,0) = 12.3f;
    m(3,2,1) = 45.6f;
    std::cout << m(0,0,0) << " " << m(3,2,1) << "\n";
}

The Boost.MultiArray library does essentially the same thing as this (and much more), but can be used for any dimension N.

Upvotes: 1

Dietmar K&#252;hl
Dietmar K&#252;hl

Reputation: 153830

If the dimensions are known at compile-time you can just allocate the object using new although I would probably put it int a structure because I keep mixing types up bewteen arrays and pointer (I barely ever use them directly):

struct array3d {
    double array[200][200][200];
};

std::auto_ptr<areay3d> array(new array3d);

Obviously, the array dimensions can become template arguments.

If the dimensions are determined only at run-time you'd need to allocate a contiguous array of double and do the array subscript computations yourself. This probably would also become a set if classes accessibg the elements: the subscript operator for the 3d array would return a reference to a 2d array, etc. std::valarray<double> is meant to help with this and there are boost classes for this as well.

Upvotes: 1

Related Questions