1

I'm new to C++, so I decided to work on some little project to improve myself. I try to write a simple chess program with class Unit, and class King which is inherited from Unit

    #include <list>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>

#include <stdlib.h>     /* abs */

using namespace std;

// Each unit class represent a chess unit
class Unit{
 protected:
    int currentX;
    int currentY;
    string side;
public:
    Unit();
    Unit(string sideplay, int Xpos,int Ypos)
    {
         currentX=Xpos; currentY= Ypos;side=sideplay;
    }

    int getX()
    {
        return currentX;
    }
    int getY()
    {
        return currentY;
    }
    string getside()
    {
        return side;
    }
    void setpos(int newX,int newY)  //set new position
    {
        currentX=newX;
        currentY=newY;
    }



     bool validmove(vector<Unit> unitlist ,string sidepick,int Xpos,int Ypos)
     {   int i=0;

         while(i != 3)
         {   int X=unitlist[i].getX();
             int Y=unitlist[i].getY();
             string sidetemp= unitlist[i].getside();
             if ((X==Xpos)&&(Y==Ypos)&&(sidetemp==sidepick))
             {
                 return false;
             }

             else if ((X==Xpos)&&(Y==Ypos)&&(sidetemp!=sidepick))
             {   //unitlist[i]=NULL;
                 return true;
             }
                 i++;

     }
         return true;


     }
     virtual void moveunit(vector<Unit> unitlist ,int nextX,int nextY);




};


class King: public Unit{
    public:
    King(string sideplay, int Xpos,int Ypos):Unit(sideplay,Xpos,Ypos)
    {}

     void moveunit(vector<Unit> unitlist ,int nextX,int nextY){
        int diffX=abs(nextX-currentX);
        int diffY=abs(nextY-currentY);
        if ((diffX==1)||(diffY==1))
        {   if (validmove(unitlist,side,nextX,nextY))
            {
            setpos(nextX,nextY);}

            }

}
}; 

and here is my main:

int main()
{
    vector<Unit> chessunit;
    chessunit.push_back(King("white",3,1));
    chessunit.push_back(King("black",3,2));
    chessunit.push_back(King("white",4,1));
    if (chessunit[0].validmove(chessunit,"white",3,2))
    {
        cout<<"hehe"<<endl;
    }
    chessunit[0].moveunit(chessunit,3,2);
    int k= chessunit[0].getY();
    cout<<k<<endl;


    return 0;
}

I keep getting LNK 2001 error: Unresolved external symbol for my virtual method "moveunit". How can I fix that bug ?

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • and where is the definition for moveunit? virtual doesn't mean (unless pure) that the method hasn't a definition – Marco A. Jul 25 '14 at 06:57
  • 1
    First, you **must** use a vector of (smart) pointers. Polymorphism only works with pointers (or references). Look up 'object slicing'. Second, make Unit::moveunit *pure virtual* (look it up). – n. m. could be an AI Jul 25 '14 at 06:59
  • I define it in my King class: Its role is to check if a move is valid or not. – user3875798 Jul 25 '14 at 07:02
  • @user3875798: No, in your `King` class you define `King::moveunit` not `Unit::moveunit`. – MSalters Jul 25 '14 at 07:41

2 Answers2

4

The easiest way of fixing your problem is using pointers or smart pointers: Store vector<Unit*>, vector<std::shared_ptr<Unit>> or vector<std::unique_ptr<Unit>> (thanks @rubenvb) instead of vector<Unit> and then add your kings like so:

myVector.push_back(new King...); // or
myVector.push_back(std::shared_ptr<King>(new King...)); // or
myVector.push_back(std::unique_ptr<King>(new King...));

Why?

If you allocate an object of a virtual class (e.g. Unit unit) and you want to assign an object of an implementation of that class to it, e.g.:

Unit unit;
unit = King(...);

Then you will get an error, or at least run into trouble, unless you provide a constructor for Unit that takes King as an argument or provide a sufficient move operator. That is because if you try to assign an object of a type that is not Unit to unit, the compiler and/or run-time (depending on what the back-end of your compiler is) will have a tough time figuring out how compatible the types are and what to do if things "don't fit" memory-wise and how to cope with memory layout issues.

Further Reading

Community
  • 1
  • 1
Domi
  • 22,151
  • 15
  • 92
  • 122
  • You should really recommend `std::unique_ptr` instead of immediately jumping to `std::shared_ptr`... – rubenvb Jul 25 '14 at 07:05
  • @rubenvb That's not the easiest solution since it imposes more restrictions and has more assumptions, some of which do not apply in his situation. Nevertheless, I added it, with a link to more information on which pointer type to use :) – Domi Jul 25 '14 at 07:07
  • more restrictions is usually a good thing, because it forces you to think about what you're doing. `shared_ptr` is a lazy "garbage-collection" like solution if you blindly apply it to everything. But +1 from me, good answer. – rubenvb Jul 25 '14 at 07:12
  • @rubenvb You are preaching to the choire. There is a reason as to why I added it. It just requires a tougher learning curve. Either way, thanks for your contribution! – Domi Jul 25 '14 at 07:13
  • So do I have to change vector on my moveunit method to vector ?Thanks – user3875798 Jul 25 '14 at 07:13
  • @user3875798 Try using `shared_ptr` instead. That is better C++, and not as complicated as `unique_ptr`. Here are some tutorials: [Related Stackoverflow question](http://stackoverflow.com/questions/3476938/example-to-use-shared-ptr) and [an article on using `shared_ptr` with STL collections](http://mobiarch.wordpress.com/2012/07/24/c-shared_ptr-and-stl-collections/) – Domi Jul 25 '14 at 07:19
  • 1
    BTW, you may use `std::make_shared` (`std::make_unique` is not here in C++11) instead of calling the smart-constructor with `new`. – Jarod42 Jul 25 '14 at 08:08
  • @rubenvb This _might_ be a case where `shared_ptr` is appropriate, although... For chess, I think I'd go with an immutable stateless representation of the individual pieces, with a static instance of each. In which case, of course, you wouldn't use `shared_ptr`. (And of course, any explication involving pointers here shouldn't fail to point out the change from value semantics to reference semantics. With mutable objects, the change can sometimes lead to surprises, and difficult to track down bugs.) – James Kanze Jul 25 '14 at 09:14
  • @James I'd go with an `enum chess_piece` and a `std::array<8*8, chess_piece>` or something along those lines. No need for OOP here IMHO. – rubenvb Jul 25 '14 at 09:29
  • @rubenvb That's more or less the way I implemented it when I gave it a go. But that was in Fortran, many years back. There are arguments for both approaches, but there is piece dependent behavior: a king doesn't behave like a pawn; so it's either OO or switches right and left. – James Kanze Jul 25 '14 at 09:53
1

The problem you are facing right now is due to slicing: when you add a King to the vector, it gets sliced into an instance of Unit.

One way to fix this is to turn chessunit into a vector of std::shared_ptr<Unit> and allocate units on the heap.

P.S. Since you are not defining Unit::moveunit(), make it pure virtual:

 virtual void moveunit(vector<Unit> unitlist ,int nextX,int nextY) = 0;
                                                                   ^^^
Community
  • 1
  • 1
NPE
  • 486,780
  • 108
  • 951
  • 1,012