0

I want to have classes named chessman and pawn, for example. I can't make chessman an abstract class because I need to create an 2D array of it. pawn class inherits chessman and I need to use move function of pawn when I write this:

chessman *cm = new pawn("a3", 'w');
cm->move(pos);

This code uses chessman::move. What can I do to make it use the one from pawn class? My question is very similar to this question, but my function doesn't take pawn as an argument. In Java, it's easy since you can create arrays of abstract classes, but in C++ it is just confusing.

EDIT:

Here is definition of chessman class (function already is virtual as you can see):

class chessman
{
public:
    int i;
    int j;
    string name;
    char color;
    virtual bool move(string final_pos);
};

And pawn:

class pawn : public chessman
{
public:
    pawn(string pos, char color);
    bool move(string final_pos);
};
13
  • 3
    This looks like a case for Polymorphism: cplusplus.com/doc/tutorial/polymorphism Commented Feb 18, 2015 at 16:52
  • read about virtual functions. Commented Feb 18, 2015 at 16:53
  • As I said, I can't make the function virtual because I need to create an instance of the class. I'm reading about Polymorphism, thanks for the reply. Commented Feb 18, 2015 at 16:56
  • 2
    @farukdgn You actually can make functions virtual without the need for having them abstract (pure virtual) in the base. Commented Feb 18, 2015 at 16:58
  • 1
    @farukdgn Not only can you make the function virtual, but you can probably make it pure virtual. Your difficulty lies in trying to instantiate an array of the base class which in C++ is not at all what you need. It's quite different from Java where all classes are passed around as references. Commented Feb 18, 2015 at 16:59

2 Answers 2

3

I can't make chessman an abstract class because I need to create an 2D array of it.

Yes, you can. You don't create an array of chessman objects, you create an array of pointers to chessman objects. Then chessman can be abstract (as it should be, since you should not be creating instances of chessman directly to begin with).

pawn class inherits chessman and I need to use move function of pawn when I write this

Polymorphism handles that for you. But in order to use an array of polymorphic objects, the array needs to hold pointers/references to objects that are stored elsewhere in memory, not hold the actual objects themselves.

In Java, it's easy since you can create arrays of abstract classes, but in C++ it is just confusing.

You do the exact same thing in C++. In Java, objects are reference types, so they are always referenced by pointer (the Java language simply hides that detail from you).

Try something like this:

class chessman
{
private:
    virtual bool isValidMove(string final_pos) = 0;

public:
    int i;
    int j;
    string name;
    char color;
    chessman(string aname, char acolor);
    bool move(string final_pos);
};

chessman::chessman(string aname, char acolor)
    : name(aname), color(acolor)
{
}

bool chessman::move(string final_pos)
{
    // validate that final_pos is a valid position on the board...

    // validate that final_pos is a valid position for the piece being moved...
    if (!isValidMove(final_pos))
        return false;

    // move to the position...

    return true;
}

class pawn : public chessman
{
private:
    virtual bool isValidMove(string final_pos);

public:
    pawn(string pos, char color);
};

pawn::pawn(string pos, char color)
    : chessman("pawn", color)
{
    //...
}

bool pawn::isValidMove(string final_pos)
{
    // validate that final_pos is a valid position for this pawn to move to...
    return ...;
}

class rook : public chessman
{
private:
    virtual bool isValidMove(string final_pos);

public:
    rook(string pos, char color);
};

rook::rook(string pos, char color)
    : chessman("rook", color)
{
    //...
}

bool rook::isValidMove(string final_pos)
{
    // validate that final_pos is a valid position for this rook to move to...
    return ...;
}

class knight : public chessman
{
private:
    virtual bool isValidMove(string final_pos);

public:
    knight(string pos, char color);
};

knight::knight(string pos, char color)
    : chessman("knight", color)
{
    //...
}

bool knight::isValidMove(string final_pos)
{
    // validate that final_pos is a valid position for this knight to move to...
    return ...;
}

class bishop : public chessman
{
private:
    virtual bool isValidMove(string final_pos);

public:
    bishop(string pos, char color);
};

bishop::bishop(string pos, char color)
    : chessman("bishop", color)
{
    //...
}

bool bishop::isValidMove(string final_pos)
{
    // validate that final_pos is a valid position for this bishop to move to...
    return ...;
}

class queen : public chessman
{
private:
    virtual bool isValidMove(string final_pos);

public:
    queen(string pos, char color);
};

queen::queen(string pos, char color)
    : chessman("queen", color)
{
    //...
}

bool queen::isValidMove(string final_pos)
{
    // validate that final_pos is a valid position for this queen to move to...
    return ...;
}

class king : public chessman
{
private:
    virtual bool isValidMove(string final_pos);

public:
    king(string pos, char color);
};

king::king(string pos, char color)
    : chessman("king", color)
{
    //...
}

bool king::isValidMove(string final_pos)
{
    // validate that final_pos is a valid position for this king to move to...
    return ...;
}

Then you can do somthing like this:

chessman* white_pieces[16];
chessman* black_pieces[16];

for (int i = 0; i < 8; ++i)
{
    white_pieces[i] = new pawn(...);
    black_pieces[i] = new pawn(...);
}

for (int i = 8; i < 10; ++i)
{
    white_pieces[i] = new rook(...);
    black_pieces[i] = new rook(...);
}

for (int i = 10; i < 12; ++i)
{
    white_pieces[i] = new knight(...);
    black_pieces[i] = new knight(...);
}

for (int i = 12; i < 14; ++i)
{
    white_pieces[i] = new bishop(...);
    black_pieces[i] = new bishop(...);
}

white_pieces[14] = new queen(...);
black_pieces[14] = new queen(...);

white_pieces[15] = new king(...);
black_pieces[15] = new king(...);

And move them around as needed:

white_pieces[index]->move(pos);
...
black_pieces[index]->move(pos);

And of course, don't forget to cleanup when you are done:

for (int i = 0; i < 16; ++i)
{
    delete white_pieces[i];
    delete black_pieces[i];
}

To make the cleanup automatic, you can use an array of std::auto_ptr<chessman> objects. Or, in C++11 and later, a std::vector/std::array of std::unique_ptr<chessman> objects.

Sign up to request clarification or add additional context in comments.

5 Comments

I have problems about creating 2D array of pointers. I define it with chessman *board[8][8]; and try to access with *(board[i][j]).color. But IntelliSense tells me that expression must have class type.
Use (*(board[i][j])).color or board[i][j]->color instead.
Oh sorry, my bad. But I expected *(board[i][j]).color to work. Does it not seem okay?
No, it is not OK, because of Operator Precedence. The . operator has a higher priority than the * operator, so *(board[i][j]).color is essentially the same as *((board[i][j]).color) - trying to dereference the color value instead of the chessman pointer, hence the error and why the extra explicit parenthesis are needed to tell the compiler what you want the * operator applied to. Using -> instead makes this cleaner. When using pointers, you really should be using ptr->member and not (*ptr).member.
Thanks! I already use ->, but since I'm new to C++, I usually mix things with Java.
1

In C++ you would create a vector (or possibly array in C++11) of some sort of pointer (the pointer type would be dictated by the object ownership) to a chessman, and have chessman be abstract. It's just a slightly different way of thinking about things.

(Edited as the comments made a great point): To make it select the proper move function, utilize the virtual mechanism in the base class. I would suggest to separate interface from implementation by not having public virtual functions. Instead, use a public non-virtual interface and a private or protected virtual implementation. You would use a private implementation which each child totally replaces the functionality while you would use protected when the child class needs to also invoke the parent functionality.

8 Comments

Regarding your last sentence, I disagree. In fact, I think this move is a perfect candidate for a non-virtual public function, because there are basic pos checks independent of any concrete chessman, namely checking if the position is within the board. Pseudo code: void move(pos p) { if (p not within board) then error handling else doMove(p); }.
move() should be virtual because different types of chess pieces have different rules regarding their valid moves. chessman::move() could do the basic validation of making sure the position is within the board, but then each piece should override move() to make sure the position is also a valid move for that particular type of piece.
@RemyLebeau: I am talking about making move non-virtual and have it delegate to a private virtual function. Requiring derived classes to call parent functions is error-prone. In C++, the guideline is to make virtual functions private and public functions non-virtual (except of the destructor, of course).
P.S.: This old article by Herb Sutter still sums it up nicely: gotw.ca/publications/mill18.htm
@RemyLebeau: This is wrong. In C++, you can override private virtual methods. And you should do so, too, as I said. Please refer to the article, or just try it in any compiler.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.