Cleaner alternatives to multidimensional arrays declared at runtime
At work, I am implementing a simple (x and y) grid system for a game. I declare a two dimensional array containing pointers to game objects that are created at runtime.
Firstly, that's a triple pointer declaration right there and it can be daunting for other people who are looking at my code.
Secondly, even initializing this thing looks like a huge mess. Barely readable.
My questions:
Are there any better solutions for this? One idea I had was to use a map with the key as a data pair, an x and y. It's a hell of a lot cleaner but I am wondering what performance effect that might have as opposed to going with a straightforward array where the cost of accessing is always O(1).
Thanks for your time.
Posted Mar 30, 2015, 5:38 PM UTC
I've seen this question pop up a lot in various places and I can give you a very clear answer from experience. First though I would think about if you really need a matrix structure for this particular use case since you will use it to store pointers to objects (why not just store the objects themselves, or make a object matrix, where each member is a matrix instead of the matrix containing full objects?).
Anyway, what you want is to leave behind the [][] syntax. Let go of that. operator[] takes only one argument, so making that work at least gives your first [] returning a pointer and your second [] offsetting that and dereferencing it. That's bad. Pointer jumping like that is inefficient and in this case the syntax can be better anyway.
Make a matrix class or find one that does the following:
One allocation based on rows and columns. Using a vector underneath is good. Don't worry about stack vs heap, it is only one allocation that shouldn't be inside any tight loops.
Use operator() to dereference the underlying memory. Your memory layout could be row or column major. Lets say the rows are linearly congruent. you can use data[row*columns + column] or put differently - data[y*xres + x]
You can make this return a pointer and dereference it outside the function or you can make two member functions - one returns a reference, one return a const reference and the function is declared const.
There are many advantages to this. You can pass a function to run on every member if it doesn't matter what the coordinates are, you can make sure to loop through it in a memory local way etc.
One thing: If you make it yourself, don't make a template first. Do that and you will get burned from compiler errors. Make it a class with a type and do the template conversion later.
Agreed on using operator() vs operator[]
From cppreference.com
This isn't necessarily true: it's entirely possible to have chained
[]access a contiguous block of memory. For example:Now, this example is absolutely minimal and doesn't enforce any safety, but hopefully you can see how one might flesh it out with, for example, a row_view type that implements the second level
operator[]and still keep the memory contiguous.Secondly, you can make
operator[]work as though it takes any number of arguments you like:This is the syntax I used when I recently demonstrated a matrix class with contiguous storage and that supports any number of dimensions with each dimension size determined at runtime: https://gist.github.com/bames53/3d23426afbf47dc62514
Your second trick is interesting and I will tuck that away for possible future use.
Your first example is exactly what I said in my post, operator[] returns a pointer that is itself de-referenced.
But the memory is contiguous and there's no 'pointer jumping'; my code does not disable fast, linear memory access patterns. This is in contrast to using
int**orvector<vector<int>>in order to get the chained[]syntax.It could be the same I suppose depending on how it is implemented and the compiler, so I agree that is much closer to ideal than non.
If operator[] only increments an address and returns that, then it should be the same, so I would say you are right, it is the same.