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

Started by
13 comments, last by rip-off 18 years, 5 months ago
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.
Advertisement
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.
I teleported home one night; With Ron and Sid and Meg; Ron stole Meggie's heart away; And I got Sydney's leg. <> I'm blogging, emo style
std::vector< std::vector<T> > level;
Sure, it should be possible, but you should absolutely use a vector.

Or rather a vector of vectors in this case.
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
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
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;}
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.
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
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.
Rob Loach [Website] [Projects] [Contact]

This topic is closed to new replies.

Advertisement