Teemo
Teemo

Reputation: 459

C++ Invalid conversion from interface class to child class

I am trying to solve this problem but I don't know how to make conversion from interface class to my child class. Basicly, I am trying to make array of Vehicle pointers and elements of array to be child classes pointers, then pass element by element to Counter class method which will calculate value of total passengers, but I get compiler error:

invalid conversion from 'oss:Vehicle*' to 'oss::Bike*' [-fPermissive]

Here is my code:

Vehicle.h

using namespace std;

namespace oss{

class Vehicle
{
public:
    virtual string type() = 0;
    virtual unsigned passengers() = 0;
    virtual ~Vehicle();
};

class Land_vehicle : public Vehicle{
protected:
    string typeOfVehicle;
    unsigned numberOfPassengers;
public:
    Land_vehicle();
    string type();
};

class Bike : public Land_vehicle{
public:
    Bike();
    unsigned passengers();
};

class Counter{
private:
    int totalPassengers;
public:
    Counter();
    void add(Bike*b);
    int total();
};

Vehicle.cpp

using namespace oss;

Vehicle::~Vehicle(){};

Land_vehicle::Land_vehicle(){typeOfVehicle = "Land";}
string Land_vehicle::type(){return typeOfVehicle;}

Bike::Bike(){numberOfPassengers = 1;}
unsigned Bike::passengers(){return numberOfPassengers;}

Counter::Counter(){totalPassengers = 0;}

void Counter::add(Bike b){
    cout <<"inside add bike"<<endl;
    totalPassengers += b.passengers();
}
int Counter::total(){return totalPassengers;}

Main.cpp

using namespace std;
using namespace oss;

int main()
{
    Counter c;
    Vehicle* v[] = {new Bike};

    size_t sz = sizeof v/sizeof v[0];
    for (unsigned i = 0; i < sz; ++i)
        c.add(v[i]);

    std::cout << "Total: " << c.total() << " passengers!" << std::endl;

    for (unsigned i = 0; i < sz; ++i)
        delete v[i];

    return 0;
}

Upvotes: 2

Views: 2264

Answers (1)

Christophe
Christophe

Reputation: 73376

According to your classes, every Bike and every Car are Vehicules. THis is why, whenever you need a Vehicle*, you can use as well a Bike*or aCar*`.

You make use of this in your assignment:

Vehicle* v[] = {new Bike};   // yes a Bike* can be converted to a Vehicle*

However the reverse relation is not true. Not every Vehicle is necessary a Bike. This is why you can't just use a Vehicle* when you need a Bike*. To do the reverse condition you first have to check that the Vehicle you're working with is indeed a Bike, and if it's the case you can use casting.

Fortunately, your classes are polymorphic (due to the virtual functions). So you have to use a dynamic_cast() to convert from parent (base) pointer to child (derived) pointer. But take care to check that the conversion succeeds (i.e. casted pointer isn't null).

What's wrong here ?

You experience this and related problems, when you try to add a vehicle to your counter:

    c.add(v[i]);   // v[i] is a pointer to a vehicle, but which one

In fact there are several problems with your Counter::add() :

  • first, you have a plain object as argument, not a pointer. This means that you'd need to dereference the pointer with c.add(*v[i]). But the overload require that you tell at compile time which type of object it is (i.e. there's no add(Vehicle), and if there would be one, you'd suffer from object slicing)
  • then, if you'd use a pointer instead of a plain object, you could use casting: c.add(dynamic_cast<Bike*>(v[i]);
  • finally, you'll realize that even with casting, you have a problem: in your example you vow that there's only a bike in your array. But in real code, you couldn't now for sure.

First work around

Now putting this together, here is how to modify your loop to add to the counter only bikes:

for (unsigned i = 0; i < sz; ++i) {
    if (dynamic_cast<Bike*>(v[i]))
        c.add(dynamic_cast<Bike*>(v[i]));
}

THis suppose to change the signature of add to:

void Counter::add(Bike* b){
    cout <<"inside add bike"<<endl;
    totalPassengers += b->passengers();
}

Here a live demo

And a solution

If you have polymorphic classes, it's a pitty not to benefit from polymorphism:

class Counter {
    int totalPassengers;
public:
    Counter();
    void add(Vehicle* b);
    int total();
};

And the implementation of the redesigned function:

void Counter::add(Vehicle* b){
    totalPassengers += b->passengers();
}

And this will work whatever the number of classes you derive from vehicle ! No longer need to add dozens of similar overloads of the same function.

Online demo

Upvotes: 2

Related Questions