【问题标题】:Array of objects being corrupted after using cout使用 cout 后损坏的对象数组
【发布时间】:2021-11-06 08:00:03
【问题描述】:

在我的问题被关闭几天后,我的编辑将回答 cmets 中的所有问题未获批准,我决定重新发布并进行一些更改。

我是 C++ 新手,但我是一位经验丰富的程序员。我正在尝试创建一个国际象棋引擎,就像我在 python 中所做的那样,但我希望它更快。我将我的棋盘存储为一个二维的棋子指针数组。

chessBoard.h

#include <iostream>
#include "pieces.h"

class Chess
{
private:
    Piece* board[8][8] = {};
public:
    Chess();
    void print_board();
};

pieces.h

enum PieceType {
    pawn, knight, bishop, rook, queen, king
};

enum Colour {
    white, black
};

class Piece {
protected:
    PieceType pieceType;
    Colour colour;
public:
    char symbol;
    Piece(Colour colour);
};

class Pawn : public Piece {
public:
    Pawn(Colour colour);
};

// The rest of the pieces are defined in the same way

pieces.cpp

#include "pieces.h"

Piece::Piece(Colour colour) {
    this->colour = colour;
}

Pawn::Pawn(Colour colour) : Piece(colour) {
    this->pieceType = pawn;
    this->symbol = (colour == white) ? 'P' : 'p';
}

// The rest of the pieces constructors are the same 

chessBoard.cpp

Chess::Chess()
{
    // The back row of black pieces
    Rook bq_r(black);
    Knight bq_n(black);
    Bishop bq_b(black);
    Queen bq_q(black);
    King bk_k(black);
    Bishop bk_b(black);
    Knight bk_n(black);
    Rook bk_r(black);

    board[0][0] = &bq_r;
    board[0][1] = &bq_n;
    board[0][2] = &bq_b;
    board[0][3] = &bq_q;
    board[0][4] = &bk_k;
    board[0][5] = &bk_b;
    board[0][6] = &bk_n;
    board[0][7] = &bk_r;
}

void Chess::print_board() {
    for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
            if (board[i][j]) {
                std::cout << board[i][j]->symbol;
                std::cout << " ";
            } else {
                std::cout << ". ";
            }
        }
        std::cout << std::endl;
    }
}

ma​​in.cpp

#include "chessBoard.h"

int main()
{
    Chess chess;
    chess.print_board();
    return 1;
}

我知道这是很多代码,我希望我可以只发布相关信息,但不幸的是我不知道问题出在哪里,我上一篇文章中的大多数 cmets 都要求提供更多信息。如果需要完整的代码,可以在这里找到:https://github.com/kiran-isaac/chess-engine/tree/test

预期结果

r n b q k b m r 
. . . . . . . . 
. . . . . . . . 
. . . . . . . . 
. . . . . . . . 
. . . . . . . . 
. . . . . . . . 
. . . . . . . . 

实际结果

r   �   � � � r 
. . . . . . . . 
. . . . . . . . 
. . . . . . . . 
. . . . . . . . 
. . . . . . . . 
. . . . . . . . 
. . . . . . . . 

第一辆车总是正确的。之后的一切都会随着程序的每次运行而改变。

原因似乎是板阵列中的所有内容都已损坏。一切都很好,直到它击中“printBoard”功能。一旦遇到第一个 cout,数组中的所有内容都会损坏。我可以通过让它将输出存储在一个字符串中并在后面输出它来解决这个问题,但这仍然会导致数组被破坏,尽管在我成功打印它之后。

我希望有人能提供帮助,因为我喜欢用 c++ 进行开发,我不想放弃这个项目。

这是 printBoard() 函数第 1 行中板数组值的屏幕截图。应该是这样的。

这是第一次迭代后的屏幕截图,在 printBoard() 函数的第 4 行遇到 std::cout 之后。

我在这个问题上卡了2天,研究和调试,我什么也没发现。我确定这很简单,我做错了,我只是找不到。如果您需要更多信息,请发表评论。

谢谢

【问题讨论】:

  • 乍一看,您似乎在获取临时对象的地址。这是UB。我假设当您调用 std::cout::operator&lt;&lt; 时,它会覆盖构造函数的堆栈帧,从而覆盖您的特定对象。使用智能指针和动态分配(或成员变量)
  • 来自 Python,您可能需要花一些时间阅读有关 对象生命周期 的内容。特别是自动存储。 C++ 要求您考虑对象的如何何时 达到其使用寿命。
  • 谢谢。是否可以将此作为答案发布,并举例说明我将如何做到这一点?
  • 您缺少标头保护以及定义 Chess 的完整标头。
  • 忘记公开了,抱歉

标签: c++ arrays oop pointers


【解决方案1】:

Chess::Chess() 构造函数中定义的对象 bq_r 等在构造函数完成时其生命周期结束,因此数组中充满了无法有效使用的悬空指针。

我建议改为:

#include <memory>

class Chess
{
private:
    std::unique_ptr<Piece> board[8][8] = {};
public:
    Chess();
    void print_board();
};

Chess::Chess()
{
    board[0][0] = std::make_unique<Rook>(black);
    board[0][1] = std::make_unique<Knight>(black);
    board[0][2] = std::make_unique<Bishop>(black);
    board[0][3] = std::make_unique<Queen>(black);
    board[0][4] = std::make_unique<King>(black);
    board[0][5] = std::make_unique<Bishop>(black);
    board[0][6] = std::make_unique<Knight>(black);
    board[0][7] = std::make_unique<Rook>(black);

    for (auto& pawn_pos : board[1])
         pawn_pos = std::make_unique<Pawn>(black);

    // ...
}

由于unique_ptrs 将通过删除指向实际指向其他类类型的基类Piece 的指针进行清理,因此该类需要一个虚拟析构函数:

class Piece {
public:
    // ...
    virtual ~Piece() = default;
};

【讨论】:

  • Piece 没有 virtual 析构函数,因此您可能也想将其添加到答案中。
  • 谢谢,我没有意识到c++比python难多少!
【解决方案2】:

已接受的答案通过使用动态分配来创建堆对象来解决对象生命周期问题,但应该注意基类指针上的多态性不需要动态分配(我假设您打算添加 virtual稍后运行)。

各种派生部分可能只是Chess 对象的数据成员,与动态分配相比,它通常提供更好的性能和更少的陷阱(使用unique_ptr 在板上正确移动指针也可能是不必要的麻烦):

class Chess {
private:
    Piece* board[8][8] = {};

    Rook m_white_rook;
    Knight m_white_knight;
    // ...all pieces here...

public:
    Chess() : 
        m_white_rook(Colour::white), 
        m_white_knight(Colour::white),
        // ...initialize pieces here...
    {
        board[0][0] = &m_white_rook;
        board[0][1] = &m_white_knight;
        // ...assign simple, non-owning pointers to the board...
    }
};

【讨论】:

  • 谢谢,在这种情况下这可能是一个更好的解决方案。但是,我将使用第一个,因为我需要学习如何处理这些事情
猜你喜欢
  • 2013-02-25
  • 1970-01-01
  • 1970-01-01
  • 2012-12-10
  • 1970-01-01
  • 1970-01-01
  • 2011-05-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多