Reputation: 1529
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
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
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
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