0

I am implementing the game Monopoly in C++. I am dealing with an abstract superclass BoardSquare (whose only property is the square name), with various subclasses depending on the type of game square (whether it's a property, chance, etc.)

Now since I want to represent the board as an array and the squares differ in their types, I have declared an array of BoardSquare pointers:

GameSquare** board = new GameSquare*[40];

and continued to populate each entry depending on the type:

board[1] = new Property("Old Kent Road", 0);
board[2] = new CommunityChest();
board[3] = new Property("Whitechapel Road", 0);

and so on.

Now Property, for example, has specific methods such as getPrice(). But running

board[1]->getPrice()

gives the error

‘class GameSquare’ has no member named ‘getPrice’

which tells me that the pointer is still being treated as a GameSquare. Is there a way around this? Typecasting perhaps? or a more elegant implementation altogether?

Luke Collins
  • 1,433
  • 3
  • 18
  • 36
  • You will need to check the type of your instance in order to be able to cast it into the sub type to call the underlying method. – Piou Dec 28 '16 at 17:49
  • 1
    @Piou Could you give me a syntactic example? For example I know that `board[1]` is a property, so could I run `((Property*)board[1])->getPrice()`? – Luke Collins Dec 28 '16 at 17:52
  • Another solution would be to implement a virtual method `getPrice` in `GameSquare` class that returns -1, overriding it in `Property` to return the price passed to the constructor. Then in your display logic you just check that the price is > 0 to display the info. – Piou Dec 28 '16 at 17:52
  • @Piou That is a good idea, but then I'd have to do that for each method in each subclass if I want to use it, no? – Luke Collins Dec 28 '16 at 17:53
  • Yes, the other solution implies you to check the type for each case in order to react properly and not meet runtime exceptions: `if(typeid(board[i]).name == "Property"){ ... } else if (typeid(board[i]) == "CommunityChest"){ ... } ...` – Piou Dec 28 '16 at 17:58

2 Answers2

2

This is a great example of object slicing.

Suggestion 1: Dynamic Downcast

One way to prevent it is to add the following:

Property *b1 = dynamic_cast<Property*>(board[index]);
if (b1)
{
    int b = b1->getPrice(); // Or whatever getPrice() returns;
}

However, you'd have to try the dynamic downcast for every subclass (Property, CommunityChest, etc.), and that might make the code look clunky to you.

Suggestion 2: Add getPrice() to GameSquare

As several commenters have suggested, you could add the method getPrice() to the GameSquare class. If the square isn't a Property, just return 0. This allows you to check whether something is a Property or something else.

Community
  • 1
  • 1
TriskalJM
  • 2,393
  • 1
  • 19
  • 20
1

You can certainly do what you're asking for, but the fact that you have to do this means that you're breaking the OO design rules.

I would suggest looking at each square in terms of actions a player can perform on it, such as buy, mortgage, get card, pass go. Some action may be automatic when the player lands on the square. Some may be presented in GUI.

This way you don't have to know that a square has a price - you just offer the player possible Buy action, and the action knows it's a property. If the property is owned by the player, it now gets "build house" action. And so on.

A square would have a method along the lines of vector<ActionPtr> getActionsFor(PlayerPtr player).

  • I like this solution, that's how I'd implement it in C# or JS, however I was not skilled enough to propose a code snippet in C++ – Piou Dec 28 '16 at 18:35