Search code examples
c++inheritancedowncast

C++ can you downcast class pointer conditionally?


I have a base class (robot) and depending on the arguments passed to the program I want to downcast the robot pointer to a specific derived class. the robot class has virtual functions which are defined in each of the derived classes.

So far I can create a pointer of the base class, and within a conditional statement, create a derived class which the base is then dynamically cast to. Within the conditional the pointer works as intended but after leaving the conditional statement the derived class is out of scope and the functions go back to virtual. Is there a way I can keep the pointer to the derived class?

class vehicles{
    public:
        virtual void print_state(int state_ind, ofstream& file){}
};

class hovercraft : public vehicles{
    public:
        hovercraft(
            int L_in, int W_in, 
            double start_x, double start_y, 
            double goal_x, double goal_y)
        {
            L = L_in;
            W = W_in;
            start_state.x = start_x;
            start_state.y = start_y;
            goal_state.x = goal_x;
            goal_state.y = goal_y;
            Tree.push_back(start_state);
        }

        void print_state(int state_ind, ofstream& file){
            state s = Tree[state_ind];

            file<<s.phi<<","<<
                s.u<<","<<
                s.v<<","<<
                s.r<<","<<
                s.x<<","<<
                s.y<<"\n";
        }

    private:
        struct state{
            double g=0;
            double u=0;
            double v=0;
            double r=0;
            double x=0;
            double y=0;
            double phi=0;
            int p=0;
        };

        int L,W;
        state start_state, goal_state, state_sample;
        vector<state> Tree;
    };

int main(int argc, char* argv[])
{   
    ifstream infile;
    string vehicle_name;
    vehicles* vehicle;
    int start, goal;
    for(int i=0; i < argc; i++){
        if (string(argv[i])=="-m"){
            infile.open (argv[i+1]);
        } else if (string(argv[i])=="-v"){
            vehicle_name = string(argv[i+1]);
        } 
    }

    ofstream file_out;
    file_out.open ("state.csv");
    if(vehicle_name == "hovercraft"){
        int L, W;
        double start_x, start_y, goal_x, goal_y;
        infile >> L;
        infile >> W;
        infile >> start_x;
        infile >> start_y;
        infile >> goal_x;
        infile >> goal_y;

        hovercraft my_vehicle(L,W, start_x, start_y, goal_x, goal_y);
        hovercraft* vehicle = dynamic_cast<hovercraft*>(&my_vehicle);
    } else {
        cout<<"Invalid Vehicle: "<<vehicle_name<<"\n Exiting...\n";
        return 0;
    }

    vehicle->print_state(0);
}

This is simplified from the actual code but I think it captures the issue. The result I want is for print to work the same inside the if statement and after it but after it will always print "no vehicle".


Solution

  • You're overloading the variable rptr inside the scope of your if-statements, which hides the one declared at the top of main. Your final rptr->print() is therefore undefined behavior, because you're calling a virtual function on an uninitialized pointer.

    Simply remove the overloading as follows:

    robot *rptr = nullptr;
    
    if (*argv[1] == 'h')       // <-- also fixed this (yours was broken)
    {
        rptr = new hovercraft();
        rptr->print();
    }
    else if (*argv[1] == 'q')  // <-- also fixed this (yours was broken)
    {
        rptr = new quadrotor();
        rptr->print();
    }
    else
    {
        std::cout << "Invalid vehicle input" << std::endl;
    }
    
    if (rptr)
    {
        rptr->print(); 
    }
    

    You might also want to consider using std::shared_ptr or std::unique_ptr (along with std::make_shared or std::make_unique) to correctly manage the lifetime of dynamic memory.