Sign in to follow this  
JDUK

[c++]Can you make a 2D array and then set its size at runtime?

Recommended Posts

I want to create a 2D array to hold my level map then set its dimensions when load_level(filename) is called at runtime. Is this possible? or do i need a vector? cheers.

Share this post


Link to post
Share on other sites
Yes. Here is a quick code sample to get you started. What you are looking for is dynamic memory allocation. I could explain it to you but you will find much better ressources online no doubt.



int main() {
int width = 0;
int height = 0;
Room* level;

std::cin >> width
std::cin >> height;
level = new Room[width][height];

//Do stuff.

//Do not forget this.
delete [] level;
}




Good luck [smile]

Edit: Using a vector is a very good idea too.

Share this post


Link to post
Share on other sites
Quote:
Original post by jfclavette
Yes. Here is a quick code sample to get you started. What you are looking for is dynamic memory allocation. I could explain it to you but you will find much better ressources online no doubt.


*** Source Snippet Removed ***

Good luck [smile]

Edit: Using a vector is a very good idea too.

It is especially good when you consider that your code doesn't work.

While a vector of vectors is a good starting point, boost::multi_array is a bit better, in my opinion. The vector approach suffers from the very real issue that each row can have a different length. Further, it ensures your array is spread all over the heap, destroying spacial locality. boost::multi_array solves both of those problems, as well as adding a bunch of nice functionality [like the ability to use 1-based indexing, as well as declaring arrays in both row major and column major forms]. It isn't always neccessary to solve them, of course, and the syntax is relatively tricky compared to the vector approaches, but its good to have an alternative in mind.

CM

Share this post


Link to post
Share on other sites
Also, a slightly simpler implementation of a straight rectangular array can be had by using a one-dimensional array and calculating offsets manually.

rect_array[x][y] <=> linear_array[x + rect_array.width * y]

This can natually be accomplished within a vector to give you the benefits of automatic memory management and bounds checking if either are important to you. This is basically what boost::multi_array does for you, albeit simplified considerably, so it solves the same problems and offers the same advantages if you want to code them yourself.

CM

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:

Also, a slightly simpler implementation of a straight rectangular array can be had by using a one-dimensional array and calculating offsets manually.



rect_array[x][y] linear_array[x + rect_array.width * y]


This can natually be accomplished within a vector to give you the benefits of automatic memory management and bounds checking if either are important to you. This is basically what boost::multi_array does for you, albeit simplified considerably, so it solves the same problems and offers the same advantages if you want to code them yourself.

CM


I concur. If that's too complex for you to understand here's a starting point..

This is for an array of ints... making this a template might be better.

This just points to an array you've made elsewhere and assumes the size is correct. You can make it more useful fairly easily. I just want to keep it simple to show Conner's method in action, which would probably be my choice.

class MultiArray
{
public:
MultiArray(int *some_array, unsigned int w)
{
linear_array=some_array;
width=w;
}

GetElement(unsigned int x, unsigned int y)
{
return linear_array[x + width * y];
}
private:
int *linear_array;
unsigned int width;
}

Share this post


Link to post
Share on other sites
Quote:
Original post by Conner McCloud

It is especially good when you consider that your code doesn't work.

While a vector of vectors is a good starting point, boost::multi_array is a bit better, in my opinion. The vector approach suffers from the very real issue that each row can have a different length. Further, it ensures your array is spread all over the heap, destroying spacial locality. boost::multi_array solves both of those problems, as well as adding a bunch of nice functionality [like the ability to use 1-based indexing, as well as declaring arrays in both row major and column major forms]. It isn't always neccessary to solve them, of course, and the syntax is relatively tricky compared to the vector approaches, but its good to have an alternative in mind.

CM


Thanks thats a GREAT reply :¬)
Learning the why's and why not's of each aprouch is very helpful.

As far as im aware cacheing is a very important speed boost to a system, and spacial locality is a big factor in that. Does the effect you mentioned regarding vectors impact this in a big way?
Are vectors a poor choice for games programming becuase of this?

Thanks again.

James.

Share this post


Link to post
Share on other sites
Quote:
Original post by JDUK
As far as im aware cacheing is a very important speed boost to a system, and spacial locality is a big factor in that. Does the effect you mentioned regarding vectors impact this in a big way?

It can do although it depends on a lot of factors. Note that this isn't limited to vectors. A C-style malloced array of malloced arrays suffers exactly the same problem.
Quote:
Are vectors a poor choice for games programming becuase of this?

No. vectors are a very good choice for games programming. However, using a vector of vectors to represent a rectangular 2D array is a bad choice, because the rows/columns of a rectangular 2D array are not separate entities, which is what a vector of vectors represents. Unfortunately there is no container that models rectangular arrays in the SC++L. You could use Boost.MultiArray as already mentioned, or for a lighter weight but less general solution you can simply wrap a one-dimensional vector with a two-dimensional interface, something like:
#include <iostream>
#include <vector>

template < typename Type >
class TwoDeeArray
{

private:

class RowProxy;

public:

TwoDeeArray()
:
width_(0),
height_(0)
{
}

TwoDeeArray(unsigned int width, unsigned int height)
:
width_(width),
height_(height),
data_(width * height)
{
}

TwoDeeArray(unsigned int width, unsigned int height, Type const & initialiser)
:
width_(width),
height_(height),
data_(width * height, initialiser)
{
}

RowProxy operator[](unsigned int index)
{
return RowProxy(data_, index * width_);
}

RowProxy const operator[](unsigned int index) const
{
return RowProxy(data_, index * width_);
}

private:

class RowProxy
{

public:

RowProxy(std::vector< Type > & data, int offset)
:
data_(data),
offset_(offset)
{
}

Type & operator[](unsigned int index)
{
return data_[index + offset_];
}

Type const & operator[](unsigned int index) const
{
return data_[index + offset_];
}

private:

std::vector< Type > & data_;
int const offset_;

};

unsigned int width_;
unsigned int height_;
std::vector< Type > data_;

};

int main()
{
TwoDeeArray< char > board(3, 3, ' ');
board[2][1] = 'X';
board[0][0] = 'O';
std::cout << " | | \n";
std::cout << ' ' << board[0][0] << " | " << board[0][1] << " | " << board[0][2] << " \n";
std::cout << " | | \n";
std::cout << "---+---+---\n";
std::cout << " | | \n";
std::cout << ' ' << board[1][0] << " | " << board[1][1] << " | " << board[1][2] << " \n";
std::cout << " | | \n";
std::cout << "---+---+---\n";
std::cout << " | | \n";
std::cout << ' ' << board[2][0] << " | " << board[2][1] << " | " << board[2][2] << " \n";
std::cout << " | | \n";
}

Enigma

Share this post


Link to post
Share on other sites
The standard template library (like std::vector, std::list, etc) was written by people who are much smarter then you or I. The library was designed for speed and efficiency, perfect for game development use. What you have to do is evaluate which structure is best for your use.

Share this post


Link to post
Share on other sites
All quality info guys, its nice to know the nitty-gritty on this stuff!

So if I understand rightly:

* Vectors are tight, just not as tight as some of the really specific stuff.

* Boost is good but more complicated

* Making a standard array that acts like a 2D array would be the easiest most memory efficent way.
For example you could trick an Array[9] into being an array[3][3] by getting it to address element 4 as [0][1] 5 as [1][1] and so on???
like so:
rather than being

0,1,2,3,4,5,6,7,8

you would change the interface to make it function like

0,1,2,
3,4,5,
6,7,8


is this correct or am i missing somthing ?

Share this post


Link to post
Share on other sites
You pretty much have it down. The easy way is to write a wrapper class. Two of us posted something you could start with.

Utilize that equation posted earlier and you're set.

Share this post


Link to post
Share on other sites
Me again,

2 more questions:

1) What does this operator, that Conner mentioned, do: <=>

2) If treating a 1D array like an nD array is the best way to handle multidinemsional array why does C++ not do this automaticly?

Thanks.

Share this post


Link to post
Share on other sites
Quote:
Original post by JDUK
Me again,

2 more questions:

1) What does this operator, that Conner mentioned, do: <=>

2) If treating a 1D array like an nD array is the best way to handle multidinemsional array why does C++ not do this automaticly?

Thanks.


1) He means the two expressions are equivalent.

2)Don’t know, It probably should.

Here’s a simplified multidimensional array class, the same what Enigma did only with out the [] operator syntax, which complicates the class. Incase you had trouble understanding what he was doing.

template <class T>
class Array2d
{
int width, height;
std::vector<T> data;
Array2d();
public:
Array2d(int Width, int Height);
T &Index(int row, int col);
T const &Index(int row, int col) const;
};

template <class T>
Array2d<T>::Array2d(int Width, int Height)
{
width = Width;
height = Height;
data.clear();
data.resize(width * height);
}

template <class T>
T & Array2d<T>::Index(int row, int col)
{
if(row < height && col < width)
return data[width * row + col];
else return *(data.end());
}

template <class T>
T const & Array2d<T>::Index(int row, int col) const
{
if(row < height && col < width)
return data[width * row + col];
else return *(data.end());
}




Use it like this.

int main(void)
{
Array2d<int> ARR2D(3,3);
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
ARR2D.Index(i,j) = i*3 + j;
}
}

int a;
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 3; j++)
{
a = ARR2D.Index(i,j);
}
}
}


Share this post


Link to post
Share on other sites
Quote:
Original post by JDUK

2) If treating a 1D array like an nD array is the best way to handle multidinemsional array why does C++ not do this automaticly?


i'm pretty sure that if your were to declare a 2d static array (not run time sized) it will just make a big 1d array "automatically"

int array[10][3];
array[i][j] = 3;

is probably compiled to the eqivelant of

int array[30];
array[ (10 * i) + j ] = 3;

for run time arrays its not so easy, as has been mentioned you need to do it yourself...

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this