【问题标题】:C++ Store class objects which contain polymorphic properties in a stackC ++将包含多态属性的类对象存储在堆栈中
【发布时间】:2020-06-29 00:54:01
【问题描述】:

我正在尝试从头开始制作国际象棋游戏,并在过去几个月中取得了重大进展。但是,我最近遇到了一个我无法弄清楚如何解决的问题。

代码(更新)

由于源代码太大,这里不能发,我觉得最好留下我的GitHub账号的链接,在那里可以找到:https://github.com/lbragile/chessCAMO

描述

国际象棋

包含相关的游戏标志和转弯跟踪,并保留棋盘位置堆栈以允许玩家撤消移动。

一块

包含作品的:

  • 正方形 ([0,63])
  • 移动信息(用于易位和初始兵移动)
  • 类型
  • 颜色

Piece Type 类

包括:

  • 典当
  • 骑士
  • 主教
  • 鲁克
  • 女王
  • 国王
  • 空(用于空白方块)

这些继承自基类(Piece

问题

通过这种设置,ChessPiece 是独立的类(一个不是从另一个派生的)。 但是,为了跟踪堆栈中的棋盘位置,Chess 类使用了成员变量:

stack<vector<Piece*>> board_positions;

这实际上让我可以撤消该位置,甚至可以在之后进行移动。但是,在通过我的测试用例运行我的算法后,我注意到它在涉及检查、双重检查、将死和僵局标志的情况下失败了。当我检查我的 GUI 时,这一点得到了证实。例如, 进行导致检查的动作,然后“撤消”该动作,并再次重复该动作。由于未存储前一个位置的检查标志,因此失败。

这导致了一个明显的结论,即我必须在每次移动时存储整个 Chess 对象,以便获得棋盘表示相应的必要标志。我只是不知道如何在 Chess 类中拥有一个可以将 Chess 对象存储在堆栈中的成员变量。主要原因是 Chess 本身包含 Piece 对象,这些对象是多态(虚函数),并且两个类是独立的。

任何关于如何进行的建议或有用的提示将不胜感激。

编辑#1

我使用提供的建议更新了代码。但是,我目前的问题是板 flags 似乎没有在堆栈上更新。这似乎源于~Chess()。我认为它实际上并没有破坏对象指针,而是简单地弹出它们。我尝试使用std::move() 在if 语句的pushInfo() 函数中将makeMove(int src, int dest, istream &amp;in) 中的资源从temp_chess 移动并删除析构函数中的game_info 指针,但这似乎并不能解决问题。

编辑#2

请参阅下面的答案,我使用Chess 对象的序列化/反序列化解决了这个问题。

【问题讨论】:

  • 您只需要创建另一个将位置和标志保存在一起的类或结构。然后当堆栈弹出位置和标志将回滚。
  • @Slava 感谢您的快速回复!如果我理解正确,假设我有一个 GAME 类,它的成员变量将是 stack&lt;vector&lt;Piece*&gt;&gt; board_positionsstack&lt;vector&lt;bool&gt;&gt; board_flags。那么公共成员函数将操纵这些(而不是在国际象棋中 - 对于board_positions)?如果是这样,我认为 GAME 也将与 Chess 和 Piece 课程完全分开?
  • 不,必须并行向量是个坏主意。我的意思是struct board { std::vector&lt;Piece*&gt; positions; std::vector&lt;bool&gt; flags; };,然后是std::stack&lt;board&gt; board_stack;
  • @lbragile Chess &amp; operator =(const Chess &amp; object) = default;。不,您需要实际执行此操作。您可以使用copy / swap 轻松做到这一点。另外,您需要关注rule of 3。您所做的是实现了这三个函数中的两个,并希望缺少用户定义的赋值运算符不会让您感到困惑。
  • 赏金与否 - 如果问题对未来的读者有任何价值,您应该创建minimal reproducible example 并将其放入实际问题中。

标签: c++ oop chess


【解决方案1】:

我设法通过将Chess 对象的私有成员序列化/反序列化到文件来解决问题。

我这样做的方法是创建一个单独的文件夹来存储这些文件,并向Chess 添加一个移动计数私有成员。然后我使用重载的提取和插入运算符添加了序列化功能:

chess.h

class Chess
{
public:
    // previous functions
    
    // Accessor/mutator functions for number of moves made
    int getNumMoves() { return num_moves;}
    void setNumMoves(int num_moves) {this->num_moves = num_moves;}        

    // to serialize (write the object to a file)
    friend ostream & operator << (ostream &out, const Chess &chess_object);

    // to de-serialize (read in the object from a file)
    friend istream & operator >> (istream &in, Chess &chess_object);

private:
    // previous members
    
    int num_moves;
};

chess.cpp

// puts each member field on a new line
ostream & operator << (ostream &out, const Chess &chess_object)
{
    for(const auto & elem : chess_object.getBoard())
        out << elem->getPieceType() << endl << elem->getPieceSquare() << endl << elem->getPieceColor() << endl
            << elem->getPieceMoveInfo() << endl << elem->getEnPassantLeft() << endl << elem->getEnPassantRight() << endl;

    out << chess_object.getCheck() << endl << chess_object.getDoubleCheck() << endl << chess_object.getCheckmate() << endl << chess_object.getStalemate() << endl;

    out << chess_object.getTurn() << endl;

    return out;
}


istream & operator >> (istream &in, Chess &chess_object)
{
    // delete the allocated memory and restore new data
    vector<Piece*> board = chess_object.getBoard();
    for(unsigned int i = 0; i < board.size(); i++)
        delete board[i];
    // board.clear();

    string input;
    for(auto & elem : board)
    {
        in >> input;
        switch(input[0] - '0')
        {
            case 0:
                elem = new Pawn(0, PAWN, NEUTRAL);
                break;
            case 1:
                elem = new Knight(0, KNIGHT, NEUTRAL);
                break;
            case 2:
                elem = new Bishop(0, BISHOP, NEUTRAL);
                break;
            case 3:
                elem = new Rook(0, ROOK, NEUTRAL);
                break;
            case 4:
                elem = new Queen(0, QUEEN, NEUTRAL);
                break;
            case 5:
                elem = new King(0, KING, NEUTRAL);
                break;
            default:
                elem = new Empty(0, EMPTY, NEUTRAL);    
        }

        in >> input;
        elem->setPieceSquare(stoi(input));
        in >> input;
        elem->setPieceColor((pieceColor) (input[0] - '0'));
        in >> input;
        elem->setPieceMoveInfo(input == "1");
        in >> input;
        elem->setEnPassantLeft(input == "1");
        in >> input;
        elem->setEnPassantRight(input == "1");
    }

    chess_object.setBoard(board);

    in >> input;
    chess_object.setCheck(input == "1");
    in >> input;
    chess_object.setDoubleCheck(input == "1");
    in >> input;
    chess_object.setCheckmate(input == "1");
    in >> input;
    chess_object.setStalemate(input == "1");

    in >> input;
    chess_object.setTurn((pieceColor) (input[0] - '0'));

    return in;
}

感谢大家提供的帮助!

【讨论】:

    猜你喜欢
    • 2015-03-13
    • 2021-01-19
    • 1970-01-01
    • 2010-09-30
    • 1970-01-01
    • 2011-12-28
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    相关资源
    最近更新 更多