Search code examples
c++polymorphismvirtual-functionsdynamic-dispatch

Virtual method overrides in C++


Assume we have an abstract class called Vehicle:

class Vehicle {
    virtual bool raceWith(Vehicle *anotherVehicle) = 0;
};

And we have its subclasses Bicycle and Car:

//  forward declaration
class Car;

class Bicycle : public Vehicle {
    virtual bool raceWith(Vehicle *anotherVehicle) {
        throw SomeExceptionClass();
    }

    virtual bool raceWith(Car *anotherVehicle) {
        return true;
    }

    virtual bool raceWith(Bicycle *anotherVehicle) {
        return false;
    }
};

However, this block of code throws SomeExceptionClass:

Vehicle *aBicycle = new Bicycle();
Vehicle *aCar = new Car();

aBicycle->raceWith(aCar);

What to do here? Does not C++ allow us to use polymorphic methods in this manner?

Any help will be appreciated. Thanks.

EDIT: It will be also good to provide an answer with dynamic_cast<> and decltype variants?


Solution

  • The following will race a Bicycle with a Vehicle:

    Vehicle *aBicycle = new Bicycle();
    Vehicle *aCar = new Car();
    
    aBicycle->raceWith(aCar);
    

    To race a Bicycle with a Car we need to use double dispatch.

    To make double dispatch work we use the this pointer to call the next function so that the second virtual call can resolve to the correct type of vehicle:

    class Car;
    class Bicycle;
    
    class Vehicle {
    public:
        virtual bool raceWith(Vehicle& anotherVehicle) = 0;
        virtual bool raceWith(Bicycle& anotherVehicle) = 0;
        virtual bool raceWith(Car& anotherVehicle) = 0;
    };
    
    
    class Bicycle : public Vehicle {
    public:
        virtual bool raceWith(Vehicle& anotherVehicle) override
        {
            //throw std::exception();
            return anotherVehicle.raceWith(*this);
        }
    
        virtual bool raceWith(Car& anotherVehicle)
        {
            return true;
        }
    
        virtual bool raceWith(Bicycle& anotherVehicle)
        {
            return false;
        }
    };
    
    class Car : public Vehicle {
    public:
        virtual bool raceWith(Vehicle& anotherVehicle) override
        {
            return true;
        }
    
        virtual bool raceWith(Car& anotherVehicle) override
        {
            return true;
        }
    
        virtual bool raceWith(Bicycle& anotherVehicle) override
        {
            return false;
        }
    
    };
    
    int main()
    {
    
        Vehicle *aBicycle = new Bicycle();
        Vehicle  *aCar = new Car();
    
        aBicycle->raceWith(*aCar);
    }
    

    Note the return anotherVehicle.raceWith(*this); which will perform the second virtual call.

    The functions are then called in this order:

    • main();
    • Bicycle::raceWith(Vehicle& anotherVehicle);
    • Car::raceWith(Bicycle& anotherVehicle);

    The following is the same program, but sticking with the use of pointers and exceptions as provided in the question:

    #include <exception>
    
    class Car;
    class Bicycle;
    
    class Vehicle {
    public:
        virtual bool raceWith(Vehicle* anotherVehicle) = 0;
        virtual bool raceWith(Bicycle* bicycle) = 0;
        virtual bool raceWith(Car* car) = 0;
    };
    
    
    class Bicycle : public Vehicle {
    public:
        virtual bool raceWith(Vehicle* anotherVehicle) override
        {
            //throw std::exception();
            return anotherVehicle->raceWith(this);
        }
    
        virtual bool raceWith(Car* car) override
        {
            return true;
        }
    
        virtual bool raceWith(Bicycle* bicycle) override
        {
            return false;
        }
    };
    
    class Car : public Vehicle {
    public:
        virtual bool raceWith(Vehicle* anotherVehicle) override
        {
            throw std::exception();
        }
    
        virtual bool raceWith(Car* car) override
        {
            return true;
        }
    
        virtual bool raceWith(Bicycle* bicycle) override
        {
            return false;
        }
    
    };
    
    int main()
    {
    
        Vehicle *aBicycle = new Bicycle();
        Vehicle  *aCar = new Car();
    
        aBicycle->raceWith(aCar);
    }