0

I'm trying to store multiple different derived classes from the same base class in a vector of pointers. Trying to call a function of one of those objects results in a segmentation fault. I haven't used inheritance very much, but I've tried every version I've been able to find and they either result in a segmentation fault or just calls the function of the base class.

I'm relatively new to C++ and haven't posted much before so please let me know if I'm sharing too much code, missing anything important, or messing up in any other respect (style, efficiency, etc.).

Edit: Instead of trying to return a Random or Human, the getPlayer function now just returns an int which which indicates which type of Player to create. The new code still results in a seg fault at the same point. (excluded getPlayer since it just returns an int and can no longer be the cause of the problem.)

This is where I define the base class (Player) and derived classes (Human and Random):

#include <string>
#include <iostream>
#include <limits>
#include <time.h>
#include "Othello.h"
using namespace std;

// Player interface to choose game moves
class Player {
public:
    // Selects and returns a move 
    virtual int getMove(Othello &game) {
        return 0;
    }
};

// User-operated player
class Human: public Player {
public:
    int getMove(Othello &game) {
        int move = 0;
        bool valid= false;
        
        while (!valid) {
            cout << "Select a move: " << endl;
            cout << "-> ";
            cin >> move;
            
            if (cin.good()) { valid = true; }
            
            else {
                cin.clear();
                cin.ignore(numeric_limits<streamsize>::max(),'\n');
                cout << "Invalid input. Try again.\n" << endl;
            }
        }
        return move;
    }
};

// Basic bot - selects a move at random
class Random: public Player {
public:
    int getMove(Othello &game) {
        srand(time(NULL));
        return ( 1 + rand() % game.n_moves );
    }
};

Here is the main function which results in a seg fault at the line move = players[state-1]->getMove(game):

int main() {
    
    // Select players (human, AI, random, etc.)
    // players[0] is left empty to keep the index consistent with the player id
    vector<Player*> players(2);
    int type;
    for ( int i : {1, 2} ) {
        type = getPlayer(i);
        if (type == 1) { players.push_back( new Human() ); }
        else if (type == 2) { players.push_back( new Random() ); }
    }
    
    // Load and start the game
    Othello game = loadBoard();
    int state = game.getState(1); // 1 or 2 for turn, or 0 for game over
    
    // Continue making moves until the game ends
    int move;
    int legal;
    while(state != 0) {
        game.print();
        legal = 0;
        cout << "PLAYER " << game.turn << endl;
        while (legal == 0) {
            move = players[state-1]->getMove(game);
            legal = game.doMove(move);
        }
        state = game.getState();
    }
    
    game.print();
    game.score();
    
    return 1;
}
  • `getPlayer` returns a `Player` object by value. Therefore, any attempts to return any derived class is doomed to failure, since everything except `Player` gets sliced away. See the duplicate question for more information. Additionally, the shown code attempts to dereference uninitialized pointers, resulting in undefined behavior. Only a random chance prevented the shown code from immediately crashing on the `*players[1] = getPlayer(1);` lines. There are multiple fundamental bugs related to how objects work in C++, in the shown code. – Sam Varshavchik Oct 18 '20 at 00:10
  • Ok, thanks. I see what you mean with both of those issues. I'm a bit out of my depth here, and it sounds like my whole approach is pretty flawed. Is there a better way I could go about what I'm trying to achieve with the above code? I keep trying to fix problems, but end up making it messier and more complicated, and causing more problems. – user2324350 Oct 18 '20 at 00:38
  • No meaningful suggestion can be made without additional context. If this a practice problem from a C++ textbook all the necessary information needed to correctly implement this should be given in that chapter. If this is a homework assignment, presumably it is expected to use material that's been presented in class before the assignment is given. The "better way" depends entirely on the original context of this program. – Sam Varshavchik Oct 18 '20 at 00:49
  • It's a long-running assignment (build a simple Othello or Checkers AI), with most of the details left up to us. I've made some edits (added to the question) to avoid the two issues you mentioned but still getting a seg fault. Any other thoughts? – user2324350 Oct 18 '20 at 01:01
  • Yes, use a debugger to run your code, one line at a time. Start by inspecting what actually happens to the `players` vector, in main, and what its values are, after the initialization loop finishes. If you expect to be working with C++ you must learn how to use a debugger. This is what a debugger is for. Knowing how to effectively use a debugger is a required skill for every C++ developer. – Sam Varshavchik Oct 18 '20 at 01:24
  • Ok, thanks. I've been meaning to learn that so I'll spend some time figuring it out. – user2324350 Oct 18 '20 at 02:14
  • 1
    This doesn't address the question, but once you get this working you'll find that `Random::getMove()` is very repetitious. That's because it initializes the random number generator every time it's called. Call `srand` exactly once, at the start of the program. – Pete Becker Oct 18 '20 at 15:22

1 Answers1

1

vector<Player*> players(2); declares a vector with two elements that will both be default initialized to nullptr.

Later, you add two more elements, so that players has 4 elements.

When state has a value of 1 or 2, the call to players[state-1]->getMove(game); will dereference a null pointer, resulting in your segmentation fault.

You likely want to define players to initially be empty (vector<Player*> players;), and update the comment on the line before this definition. (That comment, in its current form, seems to make no sense with how you're accessing the players vector later.)

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56