0

I'm trying to make a very simple programming language that lets someone play a battleship game for a school project in Flex/Bison. In order to store my variables, I have a map called symbol_table that takes a string for the key and has a Variable* for its value. The inheritance tree is as follows:

class Expression {
public:
    Type type;
    virtual ~Expression(){} //to make it polymorphic
};

class Variable : virtual public Expression {
public:
    char* name;
    Variable* next;

    virtual ~Variable(){ //also for polymorphism
        delete name;
        delete next;
    }
};

class Player : virtual public Variable{
public:
    std::string playerName;
    std::vector<GameBoat> playerBoats;
    Grid opponentGrid;
    Grid playerGrid;

    Player(std::string playerName){
        this->playerName = playerName;
    }

    bool addBoat(std::string boatName, char scolumn, int srow, bool vertical){
        //make new boat here and then push it back to the vector
        GameBoat newBoat(boatName);          //this works --> makes a valid boat
        if(newBoat.assignedValue){
            playerBoats.push_back(newBoat);  //SEGMENTATION FAULT HAPPENS HERE
            return true;
        }else{
            return false;
        }
    }
};

class Computer : public Player{
public:
    Computer(std::string playerName) : Player(playerName){}
};

Everything works great when I put Player pointers and Computer pointers into the map, but when I try to retrieve those values again using a dynamic_cast to downcast the base Variable* to a Player* or a Computer*, all of the attributes of the casted Player* or Computer* are NULL and thus give me a "Segmentation Fault: 11" error. I am, however, able to access the class methods that are within the Player and Computer classes.

Variable* var = get_symbol($1);

    if (var == NULL) {
        sprintf(errormsg, "%s not found.\n", $1);
        yyerror(errormsg);
    }else if(var->type == PlayerType){
        Player* myPlayer = dynamic_cast<Player*>(var);      //Cast var to Player*
        myPlayer->addBoat("aircraftcarrier", 'a', 1, true); //Enters this function
    }else if(var->type == ComputerType){
        Computer* myComputer = dynamic_cast<Computer*>(var);  //Cast var to Computer*
        myComputer->addBoat("aircraftcarrier", 'a', 1, true); //Enters this function
    }

How can I be accessing the derived class's methods but not have access to the derived class's attributes? I'm using polymorphism and dynamic_cast doesn't return a NULL value (otherwise the program would never enter the functions and give a Segmentation Fault right away).

tdon
  • 1,421
  • 15
  • 24
  • You are creating a programming language just for the purpose of creating a battleships game? – Neil Kirk Nov 05 '15 at 21:41
  • it's a school project. that's besides the point. – tdon Nov 05 '15 at 21:41
  • 1
    Can you provide a [minimal, complete, and verifiable example](http://www.stackoverflow.com/help/mcve)? I don't know what the issue is. – Barry Nov 05 '15 at 21:42
  • 1
    Seg fault happens with adding a `GameBoat` but you don't show its definition. Your code is susceptible for errors as you don't provide appropriate copy constructors and assignment for certain classes. It's strange you'd sometimes use strings and sometimes `char*` – Neil Kirk Nov 05 '15 at 21:43

1 Answers1

0

The addBoat function isn't virtual so there is no need to dereference myPlayer or myComputer before calling it, thus you don't get a segfault until later in the function.

All member functions have an implicit this parameter. You can think of addBoat as being equivalent to the free function bool addBoat(Player* this, std::string boatName, char scolumn, int srow, bool vertical). There is nothing special about this being NULL or not until you actually dereference it in the function.

To solve the actual problem you are having you should check that myPlayer and myComputer are not NULL before calling the function.

Kevin
  • 6,993
  • 1
  • 15
  • 24
  • If `this` is NULL, something bad has happened. – Neil Kirk Nov 05 '15 at 21:44
  • Yes obviously but it isn't dereferenced to call the member function. That's why the segfault happens later – Kevin Nov 05 '15 at 21:45
  • It is dereferenced. `mem_func();` is `this->mem_func();` – Neil Kirk Nov 05 '15 at 21:45
  • 1
    Nothing is accessed from the pointer to call the function. The compiler knows exactly where the function is so it just passes `this` along as the first parameter – Kevin Nov 05 '15 at 21:47
  • I'm not a language-lawyer, but my gut instinct tells me that calling any member function on a null object pointer is undefined behavior, and just happens to work (for a while) in practice. – Neil Kirk Nov 05 '15 at 21:49
  • Here's the language lawyer discussion: http://stackoverflow.com/questions/2474018/when-does-invoking-a-member-function-on-a-null-instance-result-in-undefined-beha – happydave Nov 05 '15 at 21:50
  • So what are you proposing? You think myPlayer->addBoat(); should be different? – tdon Nov 05 '15 at 21:52
  • 1
    It is actually undefined behavior, and I agree with you that it should be. This was just an explanation for why tdon's program didn't crash at the function call – Kevin Nov 05 '15 at 21:52
  • @tdon just add a check before you call the function to make sure the object is valid – Kevin Nov 05 '15 at 21:53
  • Got it. But if it's not valid (since it's obviously not working right now) how could I make the dynamic_cast return a valid object? – tdon Nov 05 '15 at 21:56
  • Is there something wrong with my inheritance that would cause a problem? – tdon Nov 05 '15 at 21:58
  • There's nothing in the code you posted that shows where you set the type of the object, but it looks like that isn't set correctly. Ideally you should use virtual functions instead of dynamic casts wherever you can. If not I suggest you make type a virtual function overridden in each derived class to return the correct type. – Kevin Nov 05 '15 at 22:01