4

I'm facing the following problem.

I implement parent Class - Vehicle, it has some derived classes, one of them - FastVehicle.

In the program I need to store a Vector of Vehicle* pointers. the pointers may point to Vehicle objects or to FastVehicle objects as well.

1) I want to be able to call the method print() for every object in the vector. The problem is that in case of FastVehicle I also want to tranfer a parameter to the function, I need to call a function with signature:

void print(int a)

I know a little bit about the virtual function mechanism, but according to my knowledge it works only if both functions have the same signature.

I would like to hear suggestions, about how to work it out.

2) In addition in the derived class FastVehicle has some unique function that it doesn't share with the parent class Vehicle. It performs a task that should be performed only for FastVehicle objects. What is the cleanest way to achieve this? I thought maybe to implement "empty" virtual function in the parent class Vehicle and implement the "real" task inside an overriding method of FastVehicle

Maybe someone can suggest a better solution.

thanks

roalz
  • 2,699
  • 3
  • 25
  • 42
Day_Dreamer
  • 3,311
  • 7
  • 34
  • 61
  • Either implement the common interface or use dynamic_cast. – Nick Zavaritsky Jan 15 '15 at 20:43
  • 1
    `dynamic_cast` is the answer if you really want derived classes to have incompatible interfaces. Defining a common interface would be much cleaner, if you can. – Mike Seymour Jan 15 '15 at 20:44
  • 1
    Having to using `dynamic_cast` is often a sign you've not thought things through enough. – OMGtechy Jan 15 '15 at 20:45
  • 1
    You should consider your design first. What can FastVehicle do that a Vehicle can't? If it just goes faster, then Vehicle should have a speed member and faster vehicles just set it higher. – Neil Kirk Jan 15 '15 at 20:46
  • A similar question - also involving cars :) - can be found here: http://stackoverflow.com/questions/7200446/does-downcasting-defeat-the-purpose-of-polymorphism#comment8649522_7200446 IMO, this is a perfectly legitimate use-case for downcasting. – SSJ_GZ Jan 16 '15 at 07:17

5 Answers5

1

You can always use a dynamic_cast to cast Vehicle to FastVehicle. It returns NULL if Vehicle is not FastVehicle. It depends on your use situation if you should really do this.

for(Vehicle* vehicle : vehicleVector)
{
    FastVehicle* fastVehicle = dynamic_cast<FastVehicle*>(vehicle);

    if(fastVehicle)
    {
        fastVehicle->print(1337);
        fastVehicle->somethingElse();
    }
    else
    {
        vehicle->print();
    }
}

Full example available here: https://ideone.com/69n6Jb

Scintillo
  • 1,634
  • 1
  • 15
  • 29
1

The pragmatic solutions are:

  1. Pass the int a parameter to the virtual print method but ignore it in Vehicle and only use it in FastVehicle

  2. As you suggest, simply add an "empty" virtual function to the base class that is a no-op in Vehicle and is only implemented in FastVehicle

E.g:

struct Vehicle {
  virtual ~Vehicle(){}
  virtual void print(int /*a*/) const { std::cout << "Vehicle print\n"; }
  virtual void somethingElse() { /* no-op */ }
};

struct FastVehicle : Vehicle {
  void print(int a) const override {std::cout << "FastVehicle print " << a << "\n";}
  void somethingElse() override { std::cout << "Something else!\n"; }
};

for (auto vehicle : vehicles) {
  vehicle->print(512);
  vehicle->somethingElse();
}

Live demo

Chris Drew
  • 14,926
  • 3
  • 34
  • 54
1

Most likely you have to rethink why you need a parameter for FastVehicle, but not for anything other type of Vehicle. That to me is an indication of bad design.

Just declare print(int) in the base class, override it, but in the classes where you don't need the int, just disregard it.

ventsyv
  • 3,316
  • 3
  • 27
  • 49
0

Perhaps you could refactor with an abstract vehicleI:

struct vehicleI {
    ....
    virtual void print(int) = 0;
}

and then your vehicle:

struct vehicle : vehicleI {
    ....
    void print(int i = 0);
}

and your fastVehicle as:

struct fastvehicle: vehicleI {
    ....
    void print(int);
}
Paul Evans
  • 27,315
  • 3
  • 37
  • 54
  • 1
    Note that the `i=0` default parameter will not be used if you call print on a `vehicleI` pointer as the default parameter is based on the static type. Personally, I would avoid the default parameter entirely to avoid confusion. – Chris Drew Jan 15 '15 at 21:02
  • @ChrisDrew good point, should be as `printf` and accept a variable amount of parameters. Or be a stream of some sort... – Paul Evans Jan 15 '15 at 21:32
0

if you want to properly use a dynamic call to a Vehicle interface, you need to define a common interface. If you need to specify a parameter in case of FastVehicle but not in the case of FastVehicle, that's not an interface anymore.

You have two solutions:

Default parameter

struct Vehicle
{
  virtual void print(int a=0) {};
};

struct FastVehicle : public Vehicle
{
  void print(int a=0) override {};
};

now you can call both with or without a parameter.

Second option:

struct Vehicle
{
  virtual void print() {};
};

struct FastVehicle : public Vehicle
{
  void print() override {};
  void setA(int a) { _a = a; }
  _a{};
};

Now you can set your 'a' variable through another method, but not when you access the object through Vehicle's interface.

dau_sama
  • 4,247
  • 2
  • 23
  • 30