Почему вызывается не версия производного класса функции?

#c #pointers #inheritance #vector #segmentation-fault

#c #указатели #наследование #вектор #ошибка сегментации

Вопрос:

Я пытаюсь сохранить несколько разных производных классов из одного и того же базового класса в векторе указателей. Попытка вызвать функцию одного из этих объектов приводит к ошибке сегментации. Я не очень часто использовал наследование, но я перепробовал все версии, которые смог найти, и они либо приводят к ошибке сегментации, либо просто вызывают функцию базового класса.

Я относительно новичок в C и не публиковал много раньше, поэтому, пожалуйста, дайте мне знать, если я делюсь слишком большим количеством кода, пропускаю что-то важное или что-то путаю в любом другом отношении (стиль, эффективность и т. Д.).

Редактировать: вместо того, чтобы пытаться вернуть случайное или человеческое значение, функция getPlayer теперь просто возвращает значение int, которое указывает, какой тип проигрывателя создавать. Новый код по-прежнему приводит к ошибке seg в той же точке. (исключен getPlayer, поскольку он просто возвращает значение int и больше не может быть причиной проблемы.)

Здесь я определяю базовый класс (Player) и производные классы (Human и 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 amp;game) {
        return 0;
    }
};

// User-operated player
class Human: public Player {
public:
    int getMove(Othello amp;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 amp;game) {
        srand(time(NULL));
        return ( 1   rand() % game.n_moves );
    }
};
  

Вот основная функция, которая приводит к ошибке seg в строке 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;
}
  

Комментарии:

1. getPlayer возвращает Player объект по значению. Поэтому любые попытки вернуть любой производный класс обречены на неудачу, поскольку все, кроме Player , удаляется. Для получения дополнительной информации см. Повторяющийся вопрос. Кроме того, показанный код пытается разыменовать неинициализированные указатели, что приводит к неопределенному поведению. Только случайная случайность предотвратила немедленный сбой показанного кода в *players[1] = getPlayer(1); строках. В показанном коде есть несколько фундаментальных ошибок, связанных с тем, как объекты работают в C .

2. Хорошо, спасибо. Я понимаю, что вы имеете в виду с обеими этими проблемами. Я немного не в своей тарелке, и похоже, что весь мой подход довольно ошибочен. Есть ли лучший способ добиться того, чего я пытаюсь достичь с помощью приведенного выше кода? Я продолжаю пытаться исправить проблемы, но в конечном итоге делаю это более запутанным и сложным и вызываю больше проблем.

3. Никакое значимое предложение не может быть сделано без дополнительного контекста. Если это практическая задача из учебника по C , вся необходимая информация, необходимая для правильной реализации этого, должна быть предоставлена в этой главе. Если это домашнее задание, предположительно, ожидается, что в нем будет использоваться материал, который был представлен в классе перед заданием. «Лучший способ» полностью зависит от исходного контекста этой программы.

4. Это длительное задание (создание простого ИИ «Отелло» или «Шашки»), большинство деталей остается за нами. Я внес некоторые изменения (добавил к вопросу), чтобы избежать двух упомянутых вами проблем, но все еще получаю ошибку seg. Есть еще мысли?

5. Это не решает вопрос, но как только вы получите эту работу, вы обнаружите, что Random::getMove() это очень повторяющееся. Это потому, что он инициализирует генератор случайных чисел при каждом вызове. Вызывается srand ровно один раз, в начале программы.

Ответ №1:

vector<Player*> players(2); объявляет вектор с двумя элементами, которые оба будут инициализированы по умолчанию nullptr .

Позже вы добавляете еще два элемента, так что players получается 4 элемента.

Когда state имеет значение 1 или 2 , вызов players[state-1]->getMove(game); разыменовывает нулевой указатель, что приводит к ошибке сегментации.

Вероятно, вы хотите определить players , чтобы изначально быть пустым ( vector<Player*> players; ) , и обновить комментарий в строке перед этим определением. (Этот комментарий в его текущей форме, похоже, не имеет никакого смысла в том, как вы позже получите доступ к вектору players.)