I would like to find out safe ways of implementing three dimensional arrays of integers in C++, using pointer arithmetic / dynamic memory allocation, or, alternatively using STL techniques such as vectors.
Essentially I want my integer array dimensions to look like:
[ x ][ y ][ z ]
x and y are in the range 20-6000
z is known and equals 4.
Have a look at the Boost multi-dimensional array library. Here's an example (adapted from the Boost documentation):
#include "boost/multi_array.hpp"
int main() {
// Create a 3D array that is 20 x 30 x 4
int x = 20;
int y = 30;
int z = 4;
typedef boost::multi_array<int, 3> array_type;
typedef array_type::index index;
array_type my_array(boost::extents[x][y][z]);
// Assign values to the elements
int values = 0;
for (index i = 0; i != x; ++i) {
for (index j = 0; j != y; ++j) {
for (index k = 0; k != z; ++k) {
my_array[i][j][k] = values++;
}
}
}
}
Each pair of square brackets is a dereferencing operation (when applied to a pointer). As an example, the following pairs of lines of code are equivalent:
x = myArray[4];
x = *(myArray+4);
x = myArray[2][7];
x = *((*(myArray+2))+7);
To use your suggested syntax you are simply dereferencing the value returned from the first dereference.
int*** myArray = (some allocation method, keep reading);
//
// All in one line:
int value = myArray[x][y][z];
//
// Separated to multiple steps:
int** deref1 = myArray[x];
int* deref2 = deref1[y];
int value = deref2[z];
To go about allocating this array, you simply need to recognise that you don't actually have a three-dimensional array of integers. You have an array of arrays of arrays of integers.
// Start by allocating an array for array of arrays
int*** myArray = new int**[X_MAXIMUM];
// Allocate an array for each element of the first array
for(int x = 0; x < X_MAXIMUM; ++x)
{
myArray[x] = new int*[Y_MAXIMUM];
// Allocate an array of integers for each element of this array
for(int y = 0; y < Y_MAXIMUM; ++y)
{
myArray[x][y] = new int[Z_MAXIMUM];
// Specify an initial value (if desired)
for(int z = 0; z < Z_MAXIMUM; ++z)
{
myArray[x][y][z] = -1;
}
}
}
Deallocating this array follows a similar process to allocating it:
for(int x = 0; x < X_MAXIMUM; ++x)
{
for(int y = 0; y < Y_MAXIMUM; ++y)
{
delete[] myArray[x][y];
}
delete[] myArray[x];
}
delete[] myArray;
Below is a straightforward way to create 3D arrays using C or C++ in one chunk of memory for each array. No need to use BOOST (even if it's nice), or to split allocation between lines with multiple indirection (this is quite bad as it usually gives big performance penalty when accessing data and it fragments memory).
The only thing to understand is that there is no such thing as multidimensional arrays, just arrays of arrays (of arrays). The innermost index being the farthest in memory.
#include <stdio.h>
#include <stdlib.h>
int main(){
{
// C Style Static 3D Arrays
int a[10][20][30];
a[9][19][29] = 10;
printf("a[9][19][29]=%d\n", a[9][19][29]);
}
{
// C Style dynamic 3D Arrays
int (*a)[20][30];
a = (int (*)[20][30])malloc(10*20*30*sizeof(int));
a[9][19][29] = 10;
printf("a[9][19][29]=%d\n", a[9][19][29]);
free(a);
}
{
// C++ Style dynamic 3D Arrays
int (*a)[20][30];
a = new int[10][20][30];
a[9][19][29] = 10;
printf("a[9][19][29]=%d\n", a[9][19][29]);
delete [] a;
}
}
For your actual problem, as there potentially is two unknown dimensions, there is a problem with my proposal at it allow only one unknown dimension. There is several ways to manage that.
The good news is that using variables now works with C, it is called variable length arrays. You look here for details.
int x = 100;
int y = 200;
int z = 30;
{
// C Style Static 3D Arrays
int a[x][y][z];
a[99][199][29] = 10;
printf("a[99][199][29]=%d\n", a[99][199][29]);
}
{
// C Style dynamic 3D Arrays
int (*a)[y][z];
a = (int (*)[y][z])malloc(x*y*z*sizeof(int));
a[99][199][29] = 10;
printf("a[99][199][29]=%d\n", a[99][199][29]);
free(a);
}
If using C++ the simplest way is probably to use operator overloading to stick with array syntax:
{
class ThreeDArray {
class InnerTwoDArray {
int * data;
size_t y;
size_t z;
public:
InnerTwoDArray(int * data, size_t y, size_t z)
: data(data), y(y), z(z) {}
public:
int * operator [](size_t y){ return data + y*z; }
};
int * data;
size_t x;
size_t y;
size_t z;
public:
ThreeDArray(size_t x, size_t y, size_t z) : x(x), y(y), z(z) {
data = (int*)malloc(x*y*z*sizeof data);
}
~ThreeDArray(){ free(data); }
InnerTwoDArray operator [](size_t x){
return InnerTwoDArray(data + x*y*z, y, z);
}
};
ThreeDArray a(x, y, z);
a[99][199][29] = 10;
printf("a[99][199][29]=%d\n", a[99][199][29]);
}
The above code has some indirection cost for accessing InnerTwoDArray (but a good compiler can probably optimize it away) but uses only one memory chunk for array allocated on heap. Which is usually the most efficient choice.
Obviously even if the above code is still simple and straightforward, STL or BOOST does it well, hence no need to reinvent the wheel. I still believe it is interesting to know it can be easily done.
With vectors:
std::vector< std::vector< std::vector< int > > > array3d;
Every element is accessible wit array3d[x][y][z] if the element was already added. (e.g. via push_back)
It should be noted that, for all intents and purposes, you are dealing with only a 2D array, because the third (and least significant) dimension is known.
Using the STL or Boost are quite good approaches if you don't know beforehand how many entries you will have in each dimension of the array, because they will give you dynamic memory allocation, and I recommend either of these approaches if your data set is to remain largely static, or if it to mostly only receive new entries and not many deletions.
However, if you know something about your dataset beforehand, such as roughly how many items in total will be stored, or if the arrays are to be sparsely populated, you might be better off using some kind of hash/bucket function, and use the XYZ indices as your key. In this case, assuming no more than 8192 (13 bits) entries per dimension, you could get by with a 40-bit (5-byte) key. Or, assuming there are always 4 x Z entries, you would simply use a 26-bit XY key. This is one of the more efficient trade-offs between speed, memory usage, and dynamic allocation.
There are many advantages to using the STL to manage your memory over using new/delete. The choice of how to represent your data depends on how you plan to use it. One suggestion would be a class that hides the implementation decision and provides three dimensional get/set methods to a one dimensional STL vector.
If you really believe you need to create a custom 3d vector type, investigate Boost first.
// a class that does something in 3 dimensions
class MySimpleClass
{
public:
MySimpleClass(const size_t inWidth, const size_t inHeight, const size_t inDepth) :
mWidth(inWidth), mHeight(inHeight), mDepth(inDepth)
{
mArray.resize(mWidth * mHeight * mDepth);
}
// inline for speed
int Get(const size_t inX, const size_t inY, const size_t inZ) {
return mArray[(inZ * mWidth * mHeight) + (mY * mWidth) + mX];
}
void Set(const size_t inX, const size_t inY, const size_t inZ, const int inVal) {
return mArray[(inZ * mWidth * mHeight) + (mY * mWidth) + mX];
}
// doing something uniform with the data is easier if it's not a vector of vectors
void DoSomething()
{
std::transform(mArray.begin(), mArray.end(), mArray.begin(), MyUnaryFunc);
}
private:
// dimensions of data
size_t mWidth;
size_t mHeight;
size_t mDepth;
// data buffer
std::vector< int > mArray;
};
Pieter's suggestion is good of course, but one thing you've to bear in mind is that in case of big arrays building it may be quite slow. Every time vector capacity changes, all the data has to be copied around ('n' vectors of vectors).
Related
So far, the only way that I have figured out to rotate an NxM (N not necessarily equal to M) matrix clockwise (when it is represented as a one-dimensional vector with height and width variables stored separately) is as follows:
struct matrix
{
vector<int> data;
int height;
int width;
void rotate_90()
{
vector<int> newdata(height*width);
for(int index = 0; index < height*width; index++)
{
int x = index % width;
int y = index/width; // integer division
int nextindex = (x+1)*height - 1 - y;
newdata[nextindex] = data[index];
}
data = newdata;
int temp = height;
height = width;
width = temp;
}
};
While this method does work, I'm convinced that there is a far more efficient way (specifically in terms of saving time; space is NOT a concern). Having to create a whole new vector and then overwrite the old one with the new one just doesn't sit well with me. Is there a more efficient solution to this?
Remember, what I have provided above is just for illustration. The data vector in my actual code uses objects instead of ints; using ints was just to make it easier to test. Hence, a linear algebra library like Eigen is not going to help here.
If possible I would try to avoid copying the data completely and only transform the indices when accessing elements:
struct matrix {
vector<int> data;
int height;
int width;
int& at(int x,int y) { return data(x + y*width); }
struct rotated_view {
matrix& base;
rotated_matrix_view(matrix& base) : base(base) {}
int& at(int x,int y) { return base.at(y,base.height-x-1); }
}
rotated_view rotated() { return rotated_view(*this); }
};
Note that depending on your access pattern this can have rather poor performance. On the other hand, accessing elements in the original matrix column-wise is almost as inefficient as accessing them row-wise via the rotated_matrix_view. If you do care about performance (of course you do, otherwise why would you use C++ ;) I would suggest you to try both, index transformation and actual rotation, to see what is better.
I am a Fortran user and do not know C++ well enough. I need to make some additions into an existing C++ code. I need to create a 2d matrix (say A) of type double whose size (say m x n) is known only during the run. With Fortran this can be done as follows
real*8, allocatable :: A(:,:)
integer :: m, n
read(*,*) m
read(*,*) n
allocate(a(m,n))
A(:,:) = 0.0d0
How do I create a matrix A(m,n), in C++, when m and n are not known at the time of compilation? I believe the operator new in C++ can be useful but not not sure how to implement it with doubles. Also, when I use following in C++
int * x;
x = new int [10];
and check the size of x using sizeof(x)/sizeof(x[0]), I do not have 10, any comments why?
To allocate dynamically a construction similar to 2D array use the following template.
#include <iostream>
int main()
{
int m, n;
std::cout << "Enter the number of rows: ";
std::cin >> m;
std::cout << "Enter the number of columns: ";
std::cin >> n;
double **a = new double * [m];
for ( int i = 0; i < m; i++ ) a[i] = new double[n]();
//...
for ( int i = 0; i < m; i++ ) delete []a[i];
delete []a;
}
Also you can use class std::vector instead of the manually allocated pointers.
#include <iostream>
#include <vector>
int main()
{
int m, n;
std::cout << "Enter the number of rows: ";
std::cin >> m;
std::cout << "Enter the number of columns: ";
std::cin >> n;
std::vector<std::vector<double>> v( m, std::vector<double>( n ) );
//...
}
As for this code snippet
int * x;
x = new int [10];
then x has type int * and x[0] has type int. So if the size of the pointer is equal to 4 and the size of an object of type int is equal also to 4 then sizeof( x ) / sizeof( x[0] ) will yields 1. Pointers do not keep the information whether they point to only a single object or the first object pf some sequence of objects.
I would recommend using std::vector and avoid all the headache of manually allocating and deallocating memory.
Here's an example program:
#include <iostream>
#include <vector>
typedef std::vector<double> Row;
typedef std::vector<Row> Matrix;
void testMatrix(int M, int N)
{
// Create a row with all elements set to 0.0
Row row(N, 0.0);
// Create a matrix with all elements set to 0.0
Matrix matrix(M, row);
// Test accessing the matrix.
for ( int i = 0; i < M; ++i )
{
for ( int j = 0; j < N; ++j )
{
matrix[i][j] = i+j;
std::cout << matrix[i][j] << " ";
}
std::cout << std::endl;
}
}
int main()
{
testMatrix(10, 20);
}
The formal C++ way of doing it would be this:
std::vector<std::vector<int>> a;
This creates container which contains a zero size set of sub-containers. C++11/C++13 provide std::array for fixed-sized containers, but you specified runtime sizing.
We now have to impart our dimensions on this and, unfortunately. Lets assign the top-level:
a.resize(10);
(you can also push or insert elements)
What we now have is a vector of 10 vectors. Unfortunately, they are all independent, so you would need to:
for (size_t i = 0; i < a.size(); ++i) {
a[i].resize(10);
}
We now have a 10x10. We can also use vectors constructor:
std::vector<std::vector<int>> a(xSize, std::vector<int>(ySize)); // assuming you want a[x][y]
Note that vectors are fully dynamic, so we can resize elements as we need:
a[1].push_back(10); // push value '10' onto a[1], creating an 11th element in a[1]
a[2].erase(2); // remove element 2 from a[2], reducing a[2]s size to 9
To get the size of a particular slot:
a.size(); // returns 10
a[1].size(); // returns 11 after the above
a[2].size(); // returns 9 after teh above.
Unfortunately C++ doesn't provide a strong, first-class way to allocate an array that retains size information. But you can always create a simple C-style array on the stack:
int a[10][10];
std::cout << "sizeof a is " << sizeof(a) <<'\n';
But using an allocator, that is placing the data onto the heap, requires /you/ to track size.
int* pointer = new int[10];
At this point, "pointer" is a numeric value, zero to indicate not enough memory was available or the location in memory where the first of your 10 consecutive integer storage spaces are located.
The use of the pointer decorator syntax tells the compiler that this integer value will be used as a pointer to store addresses and so allow pointer operations via the variable.
The important thing here is that all we have is an address, and the original C standard didn't specify how the memory allocator would track size information, and so there is no way to retrieve the size information. (OK, technically there is, but it requires using compiler/os/implementation specific information that is subject to frequent change)
These integers must be treated as a single object when interfacing with the memory allocation system -- you can't, for example:
delete pointer + 5;
to delete the 5th integer. They are a single allocation unit; this notion allows the system to track blocks rather than individual elements.
To delete an array, the C++ syntax is
delete[] pointer;
To allocate a 2-dimensional array, you will need to either:
Flatten the array and handle sizing/offsets yourself:
static const size_t x = 10, y = 10;
int* pointer = new int[x * y];
pointer[0] = 0; // position 0, the 1st element.
pointer[x * 1] = 0; // pointer[1][0]
or you could use
int access_2d_array_element(int* pointer, const size_t xSize, const size_t ySize, size_t x, size_t y)
{
assert(x < xSize && y < ySize);
return pointer[y * xSize + x];
}
That's kind of a pain, so you would probably be steered towards encapsulation:
class Array2D
{
int* m_pointer;
const size_t m_xSize, m_ySize;
public:
Array2D(size_t xSize, size_t ySize)
: m_pointer(new int[xSize * ySize])
, m_xSize(xSize)
, m_ySize(ySize)
{}
int& at(size_t x, size_t y)
{
assert(x < m_xSize && y < m_ySize);
return m_pointer[y * m_xSize + x];
}
// total number of elements.
size_t arrsizeof() const
{
return m_xSize * m_ySize;
}
// total size of all data elements.
size_t sizeof() const
{
// this sizeof syntax makes the code more generic.
return arrsizeof() * sizeof(*m_pointer);
}
~Array2D()
{
delete[] m_pointer;
}
};
Array2D a(10, 10);
a.at(1, 3) = 13;
int x = a.at(1, 3);
Or,
For each Nth dimension (N < dimensions) allocate an array of pointers-to-pointers, only allocating actual ints for the final dimension.
const size_t xSize = 10, ySize = 10;
int* pointer = new int*(x); // the first level of indirection.
for (size_t i = 0; i < x; ++i) {
pointer[i] = new int(y);
}
pointer[0][0] = 0;
for (size_t i = 0; i < x; ++i) {
delete[] pointer[i];
}
delete[] pointer;
This last is more-or-less doing the same work, it just creates more memory fragmentation than the former.
-----------EDIT-----------
To answer the question "why do I not have 10" you're probably compiling in 64-bit mode, which means that "x" is an array of 10 pointers-to-int, and because you're in 64-bit mode, pointers are 64-bits long, while ints are 32 bits.
The C++ equivalent of your Fortran code is:
int cols, rows;
if ( !(std::cin >> cols >> rows) )
// error handling...
std::vector<double> A(cols * rows);
To access an element of this array you would need to write A[r * rows + c] (or you could do it in a column-major fashion, that's up to you).
The element access is a bit clunky, so you could write a class that wraps up holding this vector and provides a 2-D accessor method.
In fact your best bet is to find a free library that already does this, instead of reinventing the wheel. There isn't a standard Matrix class in C++, because somebody would always want a different option (e.g. some would want row-major storage, some column-major, particular operations provided, etc. etc.)
Someone suggested boost::multi_array; that stores all its data contiguously in row-major order and is probably suitable. If you want standard matrix operations consider something like Eigen, again there are a lot of alternatives out there.
If you want to roll your own then it could look like:
struct FortranArray2D // actually easily extensible to any number of dimensions
{
FortranArray2D(size_t n_cols, size_t n_rows)
: n_cols(n_cols), n_rows(n_rows), content(n_cols * n_rows) { }
double &operator()(size_t col, size_t row)
{ return content.at(row * n_rows + col); }
void resize(size_t new_cols, size_t new_rows)
{
FortranArray2D temp(new_cols, new_rows);
// insert some logic to move values from old to new...
*this = std::move(temp);
}
private:
size_t n_rows, n_cols;
std::vector<double> content;
};
Note in particular that by avoiding new you avoid the thousand and one headaches that come with manual memory management. Your class is copyable and movable by default. You could add further methods to replicate any functionality that the Fortran array has which you need.
int ** x;
x = new int* [10];
for(int i = 0; i < 10; i++)
x[i] = new int[5];
Unfortunately you'll have to store the size of matrix somewhere else.
C/C++ won't do it for you. sizeof() works only when compiler knows the size, which is not true in dynamic arrays.
And if you wan to achieve it with something more safe than dynamic arrays:
#include <vector>
// ...
std::vector<std::vector<int>> vect(10, std::vector<int>(5));
vect[3][2] = 1;
I originally asked using nested std::array to create an multidimensional array without knowing dimensions or extents until runtime but this had The XY Problem of trying to accomplish it with std::array.
The questions One-line initialiser for Boost.MultiArray and How do I make a multidimensional array of undetermined size a member of a class in c++? and their answers give some helpful information how to use Boost::MultiArray to avoid needing to know the extents of the dimensions at runtime, but fail to demonstrate how to have a class member that can store an array (created at runtime) whose dimensions and extents are not known until runtime.
Just avoid multidimensional arrays:
template<typename T>
class Matrix
{
public:
Matrix(unsigned m, unsigned n)
: n(n), data(m * n)
{}
T& operator ()(unsigned i, unsigned j) {
return data[ i * n + j ];
}
private:
unsigned n;
std::vector<T> data;
};
int main()
{
Matrix<int> m(3, 5);
m(0, 0) = 0;
// ...
return 0;
}
A 3D access (in a proper 3D matrix) would be:
T& operator ()(unsigned i, unsigned j, unsigned k) {
// Please optimize this (See #Alexandre C)
return data[ i*m*n + j*n + k ];
}
Getting arbitrary dimensions and extent would follow the scheme and add overloads (and dimensional/extent information) and/or take advantage of variadic templates.
Having a lot of dimensions you may avoid above (even in C++11) and replace the arguments by a std::vector. Eg: T& operator(std::vector indices).
Each dimension (besides the last) would have an extend stored in a vector n (as the first dimension in the 2D example above).
Yes. with a single pointer member.
A n multidimensional array is actually a pointer. so you can alocate a dynamic n array and with casting, and put this array in the member pointer.
In your class should be something like this
int * holder;
void setHolder(int* anyArray){
holder = anyArray;
}
use:
int *** multy = new int[2][1][56];
yourClass.setHolder((int*)multy);
You can solve the problem in at least two ways, depending on your preferences. First of all - you don't need the Boost library, and you can do it yourself.
class array{
unsigned int dimNumber;
vector<unsigned int> dimSizes;
float *array;
array(const unsigned int dimNumber, ...){
va_list arguments;
va_start(arguments,dimNumber);
this->dimNumber = dimNumber;
unsigned int totalSize = 1;
for(unsigned int i=0;i<dimNumber;i++)
{
dimSizes.push_back(va_arg(arguments,double));
totalSize *= dimSizes[dimSizes.size()-1];
}
va_end(arguments);
array = new float[totalSize];
};
float getElement(unsigned int dimNumber, ...){
va_list arguments;
va_start(arguments,dimNumber);
unsgned int elementPos = 0, dimAdd = 1;
for(unsigned int i=0;i<dimNumber;i++)
{
unsigned int val = va_arg(arguments,double);
elementPos += dimAdd * val;
dimAdd *= dimsizes[i];
}
return array[elementPos]
};
};
Setting an element value would be the same, you will just have to specify the new value. Of course you can use any type you want, not just float... and of course remember to delete[] the array in the destructor.
I haven't tested the code (just wrote it straight down here from memory), so there can be some problems with calculating the position, but I'm sure you'll fix them if you encounter them. This code should give you the general idea.
The second way would be to create a dimension class, which would store a vector<dimension*> which would store sub-dimensions. But that's a bit complicated and too long to write down here.
Instead of a multidimensional array you could use a 1D-array with an equal amount of indices. I could not test this code, but I hope it will work or give you an idea of how to solve your problem. You should remember that arrays, which do not have a constant length from the time of being compiled, should be allocated via malloc() or your code might not run on other computers.
(Maybe you should create a class array for the code below)
#include <malloc.h>
int* IndexOffset; //Array which contains how many indices need to be skipped per dimension
int DimAmount; //Amount of dimensions
int SizeOfArray = 1; //Amount of indices of the array
void AllocateArray(int* output, //pointer to the array which will be allocated
int* dimLengths, //Amount of indices for each dimension: {1D, 2D, 3D,..., nD}
int dimCount){ //Length of the array above
DimAmount = dimCount;
int* IndexOffset = (int*) malloc(sizeof(int) * dimCount);
int temp = 1;
for(int i = 0; i < dimCount; i++){
temp = temp * dimLengths[i];
IndexOffset[i] = temp;
}
for(int i = 0; i < dimCount; i++){
SizeOfArray = SizeOfArray * dimLengths[i];
}
output = (int*)malloc(sizeof(int) * SizeOfArray);
}
To get an index use this:
int getArrayIndex(int* coordinates //Coordinates of the wished index as an array (like dimLengths)
){
int index;
int temp = coordinates[0];
for(int i = 1; i < DimAmount; i++){
temp = temp + IndexOffset[i-1] * coordinates[i];
}
index = temp;
return index;
}
Remember to free() your array as soon as you do not need it anymore:
for(int i = 0; i < SizeOfArray; i++){
free(output[i]);
}
free(output);
int java declaration of array like this
int a[][]=new int[3][3] works but in c++ not why? please help me i have not used c++ a long time so please help me
In C++ you would just say int a[3][3];. C++ doesn't require all arrays and objects to be declared with new.
EDIT:
For a dynamic size n you can't use stack based arrays.
Probably the best way is a vector of vectors:
std::vector<std::vector<int> > a;
a.resize(n);
for(int i = 0; i < n; ++i)
{
a[i].resize(n);
}
Generally speaking, you should avoid using arrays in C++ at all. While there are special cases where they're (nearly) the only choice, your first choice should generally be to use a std::vector instead. In this case, what you want becomes fairly straightforward:
// vector of 3 ints, each initialized to 0
std::vector<int> init(3, 0);
// vector of three vectors of int, each initialized to the value of 'init':
std::vector<std::vector<int> > a(3, init);
In C++ you can allocate arrays on the stack or on the heap. Allocation on stack is only possible for fixed-size arrays (i.e. the sizes are known at compile time):
int a[3][3];
The above allocates a 3x3 array on the stack. If you want to dynamically allocate arrays (i.e. the size is not know at compile time), it has to be done on the heap. To my knowledge however, C++ does not directly support multydimensional arrays. So you may have to do something like
int * a = new int[n*n];
And then access an element at (i,j) as a[i + j * n].
Alternatively you can also something like
int **a = new *int[n];
for(int i = 0; i < n; ++i {
a[i] = new int[n];
}
Trying to allocate a dynamic array on the stack such as
int a[n][n];
Will result in a compiler error.
In C++ you can declare a 2-dimensional int-array of a predetermined size using int a[30][10];.
You can allocate new arrays with new in Java because arrays are Objects ant thus they have to be created using new in Java. But C++ does not force you to create everything using new.
Of course, it would be no problem to introduce these new syntax for declaring arrays also in C++, but why introduce a new syntax, if "everybody" is used to the existing one?
Note that you can not declare a 2-dimensional array with sizes determined at runtime using int arr[n][m]. You have to create an array of arrays representing a 2-dimensional array using int **arr = new int[n][m] i.e. in C++ an array of pointers pointing to each subarray. Analoguesly for higher dimensional arrays.
Another way for multidimensional arrays is to declare just a 1-dimensional array and compute the indices accordingly. However, this involves some thoughts on how to organize data.
The closest match is this:
int a[][] = {
new int[3],
new int[3],
new int[3]
};
with memory management being your responsibility in C++ (unless you're using a non-standard custom new[]) -- this means you will have to call delete[] for each of elements of a.
It's best to declare it this way, though:
int a[3][3];
This will create an automatic 3x3 two-dimensional array. Unlike the first example, its memory will be allocated on the stack and thus will be deleted automatically. No need to call delete on this one.
This topic deals with two important aspects of C++: explicit pointers and dynamic memory. The short answer is that, in C++, all two need to do to initialize an array is declare it, like so:
int a [5][5];
If you want to use a variable for the array size, it must be a const int:
const int n = 5;
int b [n];
Be aware, however, that much of the functionality of arrays in Java does not exist in C++. For example, there is no straightforward "length" attribute.
The long answer is, look up the two topics addressed above, in particular in terms of arrays and the "new" keyword, as well as the "const" keyword. Understanding these ideas is vital to using C++;
I once had this same problem and ended up creating a class for it. Basically it's stored as a pointer of single dimension array and the pointers are manipulated a bit so that it acts just like a 2D array (matrix). Here's the code I used:
#include <utility>
#include <memory.h>
template <typename T>
class Matrix
{
protected:
T** m;
int x,y;
__forceinline void setMatrix()
{
assert(x > 0);
assert(y > 0);
m = new T*[y];
m[0] = new T[x*y];
for (int i = 1; i < y; ++i)
{
m[i] = m[i-1] + x;
}
}
public:
Matrix():m(0),x(0),y(0){}
Matrix(int rows, int cols):x(cols),y(rows),m(0)
{
setMatrix();
}
Matrix(const Matrix<T>& mat):m(0),x(mat.x),y(mat.y)
{
setMatrix();
memcpy_s(m[0], x*y, mat.m[0], x*y);
}
~Matrix()
{
if (m)
{
delete[] m[0];
delete[] m;
}
}
void fill(const T& val)
{
if (m)
{
for (int j = 0; j < y; ++j)
for (int i = 0; i < x; ++i)
m[j][i] = val;
}
}
T& at(int row, int col)
{
assert(row >= 0 && row < y);
assert(col >= 0 && col < x);
return m[row][col];
}
const T& at(int row, int col) const
{
assert(row >= 0 && row < y);
assert(col >= 0 && col < x);
return m[row][col];
}
T* operator[](int row)
{
assert(row >= 0 && row < y);
return m[row];
}
const T* operator[](int row) const
{
assert(row >= 0 && row < y);
m[row];
}
T& operator ()(int row, int col)
{
assert(row >= 0 && row < y);
assert(col >= 0 && col < x);
return m[row][col];
}
const T& operator ()(int row, int col) const
{
assert(row >= 0 && row < y);
assert(col >= 0 && col < x);
return m[row][col];
}
void swap(Matrix<T>& mat)
{
std::swap(m, mat.m);
std::swap(x, mat.x);
std::swap(y, mat.y);
}
const Matrix& operator = (const Matrix<T>& rhs)
{
Matrix temp(rhs);
swap(temp);
return *this;
}
//
int getRows() const
{
return y;
}
int getColumns() const
{
return x;
}
};
Usage would be like:
typedef Matrix<int> IntMatrix;
IntMatrix mat(2,3); // Creates a 2x3 matrix to store integers.
mat.fill(0); // Fill it with zeroes.
int val02 = mat[0][2]; // Unsafe way to retrieve values
int val12 = mat(1,2); // Safe way to retrieve values;
mat(0,1) = 10; // Assign values directly to the matrix.
You can also extend this class so that it has other matrix related function in it.
In my Dev C++, I am trying to create a 2D Array class that acts like a Grid.
But one of the problem is I am unsure what do for the constructor.
When I try to compile, I get the following errors:
In constructor 'Grid::Grid(int,int)':
'sqaures' is not a type
'yPos' cannot appear in a constant-expression
[Build Error] [grid.o] Error 1
Here is the Header File:
#ifndef GRID_H
#define GRID_H
using namespace std;
class Grid
{
public:
Grid(int xPos, int yPos);
// Constructor
// POST: Creates the squares of grid; (x,y) coordinates
private:
int squares;
//2D Array
//the squares; (x,y) coordinates of the grids
};
#endif
And heres the .cpp file for the functions of grid.h
#include <iostream>
#include "grid.h"
using namespace std;
Grid::Grid(int xPos, int yPos)
{
squares = new squares[xPos][yPos];
//Trying to make squares into a 2D array, and turn the values into the arguments
//into the the x,y coordinates
}
My constructor in the .cpp files doesn't work and I'm unsure what to do. Does anyone have any solutions?
Not directly related to your question, but you should not have using declarations in your header (.h/.hpp) files.
e.g.: using namespace std;
These belong in cpp files.
See Herb Sutters GOTW (Guru of the Week) #53 for reasons.
There are a few problems with your code.
First of all, your member variable "squares" should be a pointer to an int, not an int:
int *squares;
Then, the following line will give an error:
squares = new squares[xPos][yPos];
What you really need is a block of memory for the 2D array:
squares = new squares[xPos * yPos];
Also, you should save the dimensions of this array in member variables (e.g., "sizeX" and "sizeY" )
Now, you have a block of memory which will hold a 2D array of squares. I usually overload the () operator for accessing an element in this array:
int &Grid::operator() (int x, int y)
{
// you can check array boundaries here
return squares[y + sizeX*x];
}
If you have problems with the operator stuff, just create a member function instead:
int Grid::get(int x, int y)
{
// check array bounds
return squares[y + sizeX*x];
}
void Grid::set(int x, int y, int value)
{
// check array bounds
squares[y + sizeX*x] = value;
}
Finally, you need a destructor to free the memory:
Grid::~Grid()
{
delete [] squares;
}
This is how I like to do it (the "C-with-classes" style). In another answer, David Norman gives a good "Standard C++" way of implementing your class.
To avoid lots of memory issues, use a vector of vectors.
In the header
class Grid
{
...
std::vector< std::vector<squares> > squares;
...
}
In the .cpp
Grid::Grid(int xPos, int yPos)
{
squares.resize(xPos);
for (int i = 0; i < xPos; i++) {
squares[i].resize(yPos);
}
}
Later on:
squares[2][3] = square(...);
Or use a vector of vector of smart pointers if you want to new the squares.
Are you sure you got the code right? Your squares is defined as an int, not as an int**?
I'm not sure how you're getting this to compile....
EDIT:
The error message you are getting results from you defining squares as an int. Therefore, it can only take a single integer number. Your constructor is trying to assign a whole array into it. Are you familiar enough with pointers, arrays, dereferencing and all that stuff? A two dimensional array is generally tricky.
If you write your 2D array well, you can actually use a single array, and just map two-dimensional addresses into a single index.
This doesn't work:
squares = new squares[xPos][yPos];
You need:
squares = new (int*)[xPos];
for (int x = 0; x < xPos; ++x) {
squares[x] = new int[yPos];
}
And personally, that's the wrong way to do it. I prefer
class Grid {
class Row {
int* row; // this is a pointer into Grid::grid
int size; // this equals Grid::col_count and is only here for bounds checking
public:
Row(int s, int* r) : size(s), row(r) {}
int& operator[](int col) {
if (col >=0 && col < size) return row[col];
throw OutOfBoundsException();
}
};
int row_count, col_count;
int* grid;
Row* rows;
public:
Grid(int x, int y) : row_count(x), col_count(y) {
rows = new (Row*)[row_count];
grid = new int[row_count*col_count];
int* grid_walk = grid;
for (int i = 0; i < row_count; ++i) {
rows[i] = new Row(col_count, grid_walk);
grid_walk += col_count;
}
}
~Grid() { delete[] rows; delete[] grid; }
Row& operator[](int row) {
if (row ?= 0 && row < row_count) return rows[row];
throw OutOfBoundsException();
}
int rows() const { return row_count; }
int cols() const { return col_count; }
};
Grid checkers(8,8);
for (r = 0; r < checkers.row_count(); ++r) {
for (c = 0; c < checkers.col_count(); ++c) {
if ((r + c) % 2 == 1) checkers[r][c] = -1; // red space
else if (r < 3) checkers[r][c] = 1; // player 1
else if (r >= 5) checkers[r][c] = 2; // player 2
else checkers[r][c] = 0; // open square
}
}
// etc.
Hopefully there aren't too many typos.
Grid::Grid(int xPos, int yPos) {
squares = new squares[xPos][yPos];
//Trying to make squares into a 2D array, and turn the values into the arguments
//into the the x,y coordinates
}
That's of course wrong. you have to do new int[xPos][yPos] . The operator requires you to give it a type. But still then, you are not finished. yPos must be known at compile time. In your example it isn't. The reason is because it becomes part of the type that is returned by the new expression:
int (*squares)[yPos] = new int[xPos][yPos];
As types are static, yPos can't be determined at runtime. What you really want is a vector of int. But i figure you want to do the memory management yourself, because you want to learn the language rules. So go with this:
Make squares a int*: int *squares;
Change the line in the constructor into squares = new int[xPos * yPos];
Add a line like delete[] squares; into your destructor.
Add a copy constructor and copy assigment operator that copies along your memory when your instance is copied.
add a member-function like the below:
Code:
int & get(int x, int y) { return squares[y * yPos + x]; }
Which will give you the integer at the given position. Of course, you can also overload operator[] to have natural access using 2d indices:
class Grid {
struct proxy {
proxy(int *row):row(row) { }
int & operator[](int x) {
return row[x];
}
int * row;
};
int * squares;
public:
proxy operator[](int y) {
return proxy(squares + yPos * y);
}
};
The outer index will select the row, the inner will select the column. If you got to manage the memory right, you can change to better solutions. For your task, boost::multi_array is ideal: Boost.MultiArray
Other problems
Never do using namespace std; in a header file. The reason is that all code that indirectly or directly include that file will automatically also have that line included and thus see all of std::. Name conflicts will happen as soon as code tries to reference names that also happen to be defined by the C++ Standard Library.
As per David Norman's answer, use std::vector. However there is a bug in his answer, the vector should be declared as follows:
class Grid
{
...
std::vector< std::vector<int> > squares;
};
You can also initialise it using the vector constructor that takes a size and value:
Grid::Grid(int xPos, int yPos)
: squares( xPos, std::vector<int>( yPos, 0 ) )
{
}