Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
146 views
in Technique[技术] by (71.8m points)

c++ - How to instantiate abstract child class with parent class

I'm trying to make a Chess game, and I'm having difficulties with creating the objects. My thought process went something like this:

Create a game board with 64 tiles, the tiles would have their own position and a pointer to a piece, thus I would be able to "easily" load in and unload a piece.

Tile class:

class Tile {
private:
    int x;
    int y;
    Piece* piece;
public:
    Tile(int x, int y) {
        this->x = x;
        this->y = y;
        piece = nullptr;
    }
    void loadTile(Piece piece) {
        this->piece = new Piece(piece); //this gives an error
    }
    void unloadTile() {
        delete this->piece;
    }
};

The individual pieces would then inherit from a parent class (below you can see a dumbed-down version). Inherited pieces would all have the same constructor and they would only differ in the way they calculate the possible moves. This, in my mind, is the best scenario to use a virtual function.

Piece and pawn class:

class Piece {
protected:
    int x;
    int y;
public:
    Piece(int x, int y) {
        this->x = x;
        this->y = y;
    }
    virtual vector<int> returnPossibleMoves() = 0;
};

class Pawn : public Piece {
public:
    using Piece::Piece;
    vector<int> returnPossibleMoves() {
        vector<int> moves;
        moves.push_back(10); //dont think about this too much
        return moves;
    }
};

And here is the problem - the loadTile() function cannot instantiate the piece object because it is abstract.

I can see that my code may not work because I try to instantiate Piece with Pawn, but I don't really know how I would make it work, or what the workaround for this is. Hopefully you will be able to see what I'm trying to go for.

question from:https://stackoverflow.com/questions/65927262/how-to-instantiate-abstract-child-class-with-parent-class

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The Tile don't know which Piece type to instantiate, this is the fundamental problem.

What about something like this? (Disclaim, I just implemented some ideas, the code need probably lot of improvements until to get to sufficient quality)

#include <array>
#include <cassert>
#include <memory>
#include <vector>

using namespace std;

class Piece;
using CPiecePtr = std::shared_ptr<const Piece>;

enum class PieceType
{
    Pawn
};

class Pos
{
    int m_x=0;
    int m_y=0;
public:
    Pos()=default;
    Pos(const Pos&)=default;
    Pos& operator=(const Pos&)=default;
    Pos( int x, int y): m_x(x), m_y(y)
    {
        assert(x>=0 && x<8 && y>=0 && y<8);
    }
    int x() const { return m_x; }
    int y() const { return m_y; }
};

class Move
{
    Pos m_origin;
    Pos m_destination;
public:
    Move()=default;
    Move(const Move&)=default;
    Move& operator=(const Move&)=default;
    Move( const Pos& orig, const Pos& dest): m_origin(orig), m_destination(dest){}
    
    const Pos& getDestination() const { return m_destination; }
    const Pos& getOrigin() const { return m_origin; }
};
using MoveSet = std::vector<Move>;

class Tile 
{
private:
    CPiecePtr m_piece;
public:
    void loadTile(CPiecePtr piece)
    {
        m_piece = piece;
    }
    void unloadTile() 
    {
        m_piece = nullptr;
    }
    
    void setPiece(CPiecePtr piece) // this is more generic than previous two functions
    {
        m_piece = piece;
    }
    
    CPiecePtr getPiece() const 
    {
        return m_piece;
    }
};

class Piece 
{
    PieceType m_type;
public:
    virtual MoveSet returnPossibleMoves(const Pos&) const = 0;
    Piece(): m_type(PieceType::Pawn){}
    
    PieceType getType() const { return m_type; }
};

class Pawn : public Piece 
{
public:
    MoveSet returnPossibleMoves(const Pos& pos) const override
    {
        MoveSet moves;
        moves.push_back(Move(pos, Pos(pos.x(), pos.y()+1)));
        //...
        
        //TODO how to manage special moves? King-rook, replace pawn at end line...
        return moves;
    }
};

class Chess
{
private:
    std::array<std::array<Tile,8>,8> m_board;
    std::vector<CPiecePtr> m_pieces;
public:
    Chess()
    {
        m_pieces.push_back( std::make_shared<const Pawn>());
        //...
        
        setPieceAt(Pos(0,1), m_pieces[0]);
    }
    
    CPiecePtr getPieceAt( const Pos& pos) const
    {
        return m_board[pos.x()][pos.y()].getPiece();
    }
    
    void setPieceAt( const Pos& pos, CPiecePtr piece)
    {
        return m_board[pos.x()][pos.y()].setPiece(piece);
    }
    
    // example:
    MoveSet getMoveSetForPos( const Pos& pos)
    {
        const auto& piecePtr = getPieceAt(pos);
        if (nullptr != piecePtr)
        {
            return piecePtr->returnPossibleMoves(pos);
        }
        return {};
    }
    
    void movePiece( const Move& move)
    {
        const auto& prevPiece = getPieceAt(move.getOrigin());
        const auto& nextPiece = getPieceAt(move.getDestination());
        assert(prevPiece && !nextPiece);
        
        setPieceAt(move.getDestination(), prevPiece);
        setPieceAt(move.getOrigin(), nullptr);
    }
};

int main()
{
    Chess chess;
   
    const auto& moves = chess.getMoveSetForPos(Pos(0,1));
    if (moves.size()>0)
    {
       chess.movePiece(moves[0]);
    }
    
    assert( chess.getPieceAt(Pos(0,2))->getType() == PieceType::Pawn);
    
    return 0;
}

EDIT: I was not very proud of the answer, so I edited the code to make it compile. However, a fully working Chess is more complex than that, I leave how to manage king-rook and other special moves to the reader.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...