Edex
Edex

Reputation: 11

Is there a way to make a dynamic two-dimensional array in C++?

So I wanted to create a game with a square world.

When starting the game, the user should be able to specify the size of the world.

The world is saved as a two-dimensional array of shorts.

const short gameSize = 4;
short world[gameSize][gameSize];

The short gameSize must be const, otherwise, I can't put gameSize as the size of world[][].

However, this won't allow me to set gameSize to the player-wished size.

I thought of something simple like

short gameSize = 0;
cout << "World size?" << endl;
cin >> gameSize;
short world[gameSize][gameSize];

As stated, this won't work.

In the game, it won't be possible to change the value of gameSize later on.

How could I make this happen?

Upvotes: 1

Views: 177

Answers (1)

user4442671
user4442671

Reputation:

  1. Don't use new as the linked questions/answers would lead you to do. It's unnecessary in your case and will increase the risk of potential bugs for no good reason.
  2. Use std::vector<short> to manage an indexable chunk of memory storing the world values.
  3. Convert 2D coordinates into an index as needed.
  4. (optional) Encapsulate it all in a class so it's hidden
#include <vector>
#include <cassert>
#include <iostream>

class GameWorld {
  std::size_t world_size_;
  std::vector<short> data_;

public:
  GameWorld(std::size_t size) 
    : world_size_(size) 
    , data_(size * size) {}

  short& operator()(std::size_t x, std::size_t y) {
    assert(x < world_size_ && y < world_size_);

    return _data[y + x * world_size_];
  }

  const short& operator()(std::size_t x, std::size_t y) const {
    assert(x < world_size_ && y < world_size_);

    return _data[y + x * world_size_];
  }
};


int main() {
  short gameSize = 0;
  std::cout << "World size?" << std::endl;
  std::cin >> gameSize;
  GameWorld world(gameSize);

  // Set the value of [0, 0] to 4
  world(0, 0) = 4;
}

There's a few things happening here:

  1. Using vector<short> will give you a dynamic size as well as memory safety. i.e. Things will get cleaned up "automatically" as appropriate.
  2. You might be tempted to use vector<vector<short>>, so that world[x][y] "just works". But that's not great because the memory will be all over the place, packing it tightly into a one-dimensional array gives you a bunch of performance benefits in general.
  3. You might "think" that calling a function and doing math on indices is expensive, but it's actually the exact same thing the compiler does when you call world[x][y] in your previous code, so no additional cost is incurred here.
  4. The assert()s will catch potential bugs when building in debug mode, and disappear entirely in release/NDEBUG mode. So you have an additional "free" safety net .

Bonus: If you want to start dipping your toes into template programming, this is a great starting point to learn. Templating GameWorld so that it can be a world of float, int, or some struct Cell instead of always being a world of short is useful, easy to do, and not disruptive to the rest of the code.

Upvotes: 6

Related Questions