Ryan D
Ryan D

Reputation: 42

In C++, what is the proper way to inherit super classes so their sub classes don't have a duplicate property in the super and sub class?

The following code is a simplistic example of what I am trying to do. The goal is to have all the game logic in the super classes. Then, where necessary, add sub classes for platform specific functionality.

#include <iostream>

// Base Game and GameBoard classes to handle all of the core logic
class GameBoard
{
public:
    GameBoard();
    void performMove(int value);
protected:
    int board[4][4];
};
class Game
{
public:
    Game();
    GameBoard gameBoard;
    void performMove(int value);
};

// Derived Game and GameBoard classes to handle the user interaction IO.
class DerivedGameBoard : public GameBoard
{
public:
    DerivedGameBoard();
    void print();
};

class DerivedGame : public Game
{
public:
    DerivedGame();
    DerivedGameBoard gameBoard;
};

Game::Game() {
    gameBoard = GameBoard();
}

void Game::performMove(int value) {
    gameBoard.performMove(value);
}


DerivedGame::DerivedGame() {
    gameBoard = DerivedGameBoard();
}

GameBoard::GameBoard()
{
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            board[i][j] = 0;
        }
    }
}
void GameBoard::performMove(int value)
{
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            board[i][j] = value;
        }
    }
}
DerivedGameBoard::DerivedGameBoard()
{
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            board[i][j] = 1;
        }
    }
}
void DerivedGameBoard::print()
{
    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 4; ++j) {
            std::cout << board[i][j];
        }
        std::cout << std::endl;
    }
}

Here is where I am testing these class interactions. My assumed, or desired outputs are in the comments.

int main(int argc, char** argv)
{
    std::cout << "Test Derivation" << std::endl;

    DerivedGame game = DerivedGame();
    game.gameBoard.print(); // Prints all 1s as expected

    game.gameBoard.performMove(2);
    game.gameBoard.print(); // Prints all 2s as expected

    game.performMove(3);
    game.gameBoard.print(); // Prints all 2s, which is unexpected.
}

Is this type of inheritance possible in C++? Is a pointer for the board necessary in this situation? Am I missing something obvious?

Upvotes: 0

Views: 72

Answers (2)

n. m. could be an AI
n. m. could be an AI

Reputation: 120021

This is The Way (capitalized because extremely important)

 class Base
 {
   virtual BaseAttr& getAttr() = 0;
   // methods that use getAttr
 };

 class Derived final : public Base
 {
   // Covariant return type 
   DerivedAttr& getAttr() override {
     return attr;
   }
   // more methods that use getAttr
   DerivedAttr attr;
 };

This design is sometimes called "Intelligent Children design pattern".

It is important to note that your base class is abstract, and your concrete class is final (not derived any further). This is iactually a good practice to always do so. An extreme form of this design style is to only derive publicly from pure interfaces (a.k.a. pure abstract classes).

Upvotes: 3

M.M
M.M

Reputation: 141628

Firstly I'd like to say to avoid this whole design if possible. New C++ programmers over-use inheritance and here you have a class hierarchy with vtables where you don't actually need one, which adds a lot of unnecessary complication.


Anyway, if the derived classes need a different gameBoard then you will have to access the board via a base class pointer (or reference), so that you can actually allocate a different board class for each game class.

There are several ways to approach this; here is one possible skeleton:

struct BasicGame
{
    std::unique_ptr<BasicBoard> p_board;

    BasicGame(BasicBoard *new_board): p_board(new_board) {}

    virtual ~BasicGame() = 0;   // make this abstract
};

struct Game1 : BasicGame
{
     Game1(): BasicGame{ new Board1 } {}
};

struct Game2 : BasicGame
{
     Game2(): BasicGame{ new Board2 } {}
};

This way, when you construct a Game2 for example, then p_board points to a board of type Board2 (which must be derived from BasicBoard of course, and have a virtual function); and functions that accept a BasicBoard reference will be able to work with p_board.

Upvotes: 1

Related Questions