Search code examples
c++c++11abstract-classpure-virtualderiving

-std=c++11 undefined reference to vtable for -- Chess


I've an issue and I can't solve it (with google). As practice I write a program about chess. There is a general piece class:

pieces.h :

#ifndef PIECES_H
#define PIECES_H

[...]

class piece{
public:
    bool dark; //0 = light, 1 = dark
    virtual std::vector<position> sovereign_movement();
    types identify; // enum types = {pawn, knight, king, etc...}
    position pos;
    std::vector<position> horizontal(position a);
    std::vector<position> vertical(position a);
    std::vector<position> plus90(position a);      //basic moves
    std::vector<position> minus90(position a);
    std::vector<position> Lshape(position a);

    piece() : identify(non), pos(position(1,1)) {}
    virtual ~piece() {}
};

class Pawn : public piece{
public:
    Pawn() { identify = pawn; }
    std::vector<position> sovereign_movement() {std::vector<position> sol; return sol;}
    ~Pawn() = default;
};
[...]

#endif //PIECES_H

And my main for testing:

#include "pieces.h"

int main()
{
   piece a;
   vector<position> test = a.horizontal();
   for(unsigned int i=0; i<test.size() ;i++)
       {
        cout << test[i].Pconvert() << endl; /// position::Pconvert() just make string
       }
return 0;
}

I've a pieces.cpp file what define the longer methods:

#include "pieces.h"
#ifndef PIECES_H
#define PIECES_H

std::vector<position> piece::horizontal(position a){
    std::vector<position> sol;
    for(int i = 1;i<=8;i++){
        sol.push_back(position(i,a.y));
        if(i==a.x){sol.pop_back();}
    }
    return sol;
}

[...]

#endif //PIECES_H

For this I get undefined reference to piece::horizontal(position) and undefined reference to vtable for piece for constructor and destructor in header file.

If i do my destructor abstract (virtual ~piece() = 0) I get: error: cannot declare variable 'a' to be of abstract type 'piece', because the following functions are pure within 'piece': virtual piece::~piece()

After that I've write default destructors all of derived classes, but writes same. After this I modify my main this way (Pawn is one of derived classes of piece):

int main()
{
    piece* a = new Pawn();
    vector<position> test = a->horizontal(a->pos);
    for(unsigned int i=0;i<test.size();i++){
        cout << test[i].Pconvert() << endl;
    }
    return 0;
}

And then I get: undefined reference to 'piece::horizontal(position)' and undefined reference to piece's destructor in .h, so round is closed.

Could someone help me? Thank you


Solution

  • When you include a header file, the contents of that file replace the #include line. So when pieces.cpp starts like this:

    #include "pieces.h"
    #ifndef PIECES_H
    #define PIECES_H
    
    std::vector<position> piece::horizontal(position a){
        std::vector<position> sol;
        for(int i = 1;i<=8;i++){
            sol.push_back(position(i,a.y));
            if(i==a.x){sol.pop_back();}
        }
        return sol;
    }
    
    #endif //PIECES_H
    

    it becomes the following after the pre-processor handles the #include.

    #ifndef PIECES_H
    #define PIECES_H
    class piece{
    public:
        bool dark; //0 = light, 1 = dark
        virtual std::vector<position> sovereign_movement();
        types identify; // enum types = {pawn, knight, king, etc...}
        position pos;
        std::vector<position> horizontal(position a);
        std::vector<position> vertical(position a);
        std::vector<position> plus90(position a);      //basic moves
        std::vector<position> minus90(position a);
        std::vector<position> Lshape(position a);
    
        piece() : identify(non), pos(position(1,1)) {}
        virtual ~piece() {}
    };
    #endif //PIECES_H
    #ifndef PIECES_H
    #define PIECES_H
    
    std::vector<position> piece::horizontal(position a){
        std::vector<position> sol;
        for(int i = 1;i<=8;i++){
            sol.push_back(position(i,a.y));
            if(i==a.x){sol.pop_back();}
        }
        return sol;
    }
    
    #endif //PIECES_H
    

    Next, account for the PIECES_H macro. I'll comment out the preprocessing lines as I proceed.

    // #ifndef PIECES_H  -- PIECES_H is not defined yet, so proceed.
    // #define PIECES_H  -- now PIECES_H is defined.
    class piece{
    public:
        bool dark; //0 = light, 1 = dark
        virtual std::vector<position> sovereign_movement();
        types identify; // enum types = {pawn, knight, king, etc...}
        position pos;
        std::vector<position> horizontal(position a);
        std::vector<position> vertical(position a);
        std::vector<position> plus90(position a);      //basic moves
        std::vector<position> minus90(position a);
        std::vector<position> Lshape(position a);
    
        piece() : identify(non), pos(position(1,1)) {}
        virtual ~piece() {}
    };
    // #endif //PIECES_H -- end of the earlier #ifndef
    // #ifndef PIECES_H  -- PIECES_H is still defined from earlier. So skip ahead.
    // All these lines are omitted from compilation
    // #endif //PIECES_H -- end of the earlier #ifndef
    

    The preprocessor just ripped out the contents of pieces.cpp because of the mis-use of header guards. Header guards belong in the header (see also A: C++ Header Guard Syntax and Header Placement). Take them out of your source file and it should compile as intended.