tags:

views:

151

answers:

5

I have a vector with pointers of type Vehicle. Vehicle is the base class and there are many derived types like MotorCycle, Car, Plane, etc. Now, in my program there comes a point where I need the derived type while traversing the vector. Each Vehicle class has a GetType() function which returns an int which tells me what the derived type is (motorcylce, car, plan). So, I can use a dynamic cast to downcast to the derived type from the base class pointer. However, I need to have a giant if statement everytime I need the derived pointer

if(vehicle_ptr->GetType() == PLANE)
    Plane *ptr = dynamic_cast<Plane*> vehicle_ptr;
else if (vehicle_ptr->GetType() == MOTORCYCLE)
    MotorCycle *ptr = dynamic_cast<MotorCycle*> vehicle_ptr;
..and on and on.

Is there a way to have a function or some trick I can call that would save me from the giant if statement everywhere? Like ::GetDerivedPtr(Vehicle *ptr). Would a template class help here? (never used them before) Sorry, my C++ is a bit rusty and I did search but these terms bring up too much material to find what I'm looking for. Thanks.

+4  A: 

It looks like you've manually tried to recreate polymorphism. You don't need a type member. This is almost always a bad idea. Use polymorphism and virtual functions.

When you have a vehicle pointer v and do

v->function();

It will call the proper function for whatever type (Plane, Train, or Automobile) that the pointer actually points to if function is a virtual function. What you're doing is already handled by the language.

So:

class A {
public:
    virtual void f() {cout << "A";}
};

class B : public A {
public:
    virtual void f() {cout << "B";}
};

int main(){
  A *a;
  B b;
  a = &b;
  a->f();
}

The above snippet will print B.

JoshD
if you need a function that is only in the derived cast your can allways use dynamic_cast rather then the gettype parameter.
rerun
I don't understand. Yes, I know about virtual functions however I still need the derived pointer because I need access to the functions specific to the derived type at this point in the program. @rerun, I can use a dynamic_cast but I wouldn't know what to cast it to without GetType. I don't understand the answer people are suggesting.
max111
what is the difference between testing a hard coded integer to determine type and seeing if a dynamic_cast retuns you a null pointer http://en.wikipedia.org/wiki/Dynamic_cast.
rerun
Oh ok. I can remove the gettype but I'd still have to cycle through all possible derived types looking for the one cast to return !NULL right?
max111
@max111: The usual mantra is: if you need to manually discover the subtype of the object behind a pointer-to-base in order to manually call subtype-specific functions, then you need to re-think your inheritance relationship. Why can't you just have a `virtual void do_something_special()` which each subtype overrides, and performs whatever functionality you need? Perhaps you need to include this in your question.
Oli Charlesworth
+1  A: 

First check whether what you're going to do can be done simply via virtual functions in class Vehicle, overridden by each derived class.

If not, then consider the Visitor Pattern.

Cheers & hth.,

Alf P. Steinbach
Hi Alf, no way to do it via the virtual mechanism. I will look into the Visitor pattern and report back.
max111
+1  A: 

I second the idea that you need some virtual function and a common base type. Imagine that there is some way to get the pointer which has the correct type. What will you do with it then? You'll have to make a giant switch anyway, because you call specific functions for each of your specific types.

One solution would be to invent a name for the operation you are trying to execute, and put its implementation as a virtual function at each specific Vehicle class. If the operation accepts different parameter for each of the cases, the parameters have to be packed into a special polymorphic structure/class, but here maybe the Visitor pattern is a more generic solution.

Vlad
You're right Vlad. No escaping the giant if statement. Thanks for the suggestion on alternative ways to do this.
max111
You're welcome!
Vlad
A: 

dynamic_cast will check the type itself (you don't need your own variable for this). You can do the following instead:

Plane *plane_ptr = dynamic_cast<Plane*>(vehicle_ptr);
if(plane_ptr != NULL)
{
    // Do stuff with 'plane_ptr' that you couldn't do with 'vehicle_ptr'
}

I don't really see how creating a function to do the cast would help because you still need to class specific code anyway (and the function would have a fixed return type, so the closest you could get is something like the 'dynamic_cast' call, which is pretty much a standard function anyway).

Grant Peters
What is dynamic_case? Did you mean dynamic_cast? I thought that was a standard.
CiscoIPPhone
thx, i did mean dynamic_cast and yes, it is standard and built into the language itself. What I meant by a "standard function" is that the call isn't really a c++ function as it is never declared and the compiler just knows how to use it. Other than that, it can just be considered a templated function. I wasn't referring to whether it was included in standard implementations, just on how it has a somewhat special status with the compiler.
Grant Peters
A: 

Use Visitor based dispatching. Observe that not a simple cast of any kind is required in the follwing (somewhat trivialized) example:

// simple cyclic visitor
class VehicleVistor {
 public:
  // add overload for each concrete Vehicle type
  virtual void Visit(class Motorcycle&) {};
  virtual void Visit(class Plane&) {};
  virtual void Visit(class Car&) {};
};

class Vehicle {
 public:
  virtual Accept(VehicleVisitor&) = 0;
};

class Car : public Vehicle {
 public:
  virtual Accept(VehicleVisitor& pVisitor) {
   pVisitor.Visit(*this);
  }
};

// and so on...

At some point of you program you need to retrieve all instances of, say Motorcycle:

class MotorcycleExtractingVisitor : public VehicleVisitor {
  std::vector<Motorcycle*> mMotorcycles;
 public: 

  void operator()(Vehicle* pVehicle) {
   pVehicle->Accept(*this);
  }

  void Visit(Motorcycle& pMotorcycle) {
   mAllMotorcycles.push_back(pMotorcycle);
  }

  std::vector<Motorcycles*> Get() { return mAllMotorcycles; }
};

class Extractor {
public:

  // here you extract motorcycles
  static std::vector<Motorcycle*> ExtractMotorcycles(std::vector<Vehicle*>& pVehicles) {
   MotorcycleExtractingVisitor tMotos;
   std::for_each(pVehicles.begin(), pVehicles.end(), tMotos);
   return tMotos.Get();
  }

  // this would be a templatized version, left as exercise to the reader
  template<class TExtracted, classtypename TBegItr, typename TEndItr>
  static std::vector<TExtracted*> Extract(TBegItr pBeg, TEndItr pEnd) {
   ExtractingVisitor<TExtracted> tRequiredVehicles;
   std::for_each(pBeg, pEnd, tRequiredVehicles);
   return tRequiredVehicles.Get();
  }

};

Usage is as follows:

// fixed type version:
std::vector<Motorcycles*> tMotos =
  Extractor::Extract(tVehicleVector);

// templatized version (recommended)
std::vector<Motorcycles*> tMotos = 
  Extractor::Extract<Motorcycles>(
    tVehicleVector.begin(),tVehicleVector.end());
paul_71
Hi Paul, shouldn't there be call to Vehicle::Accept instead of Vehicle::Visit in MotorcycleExtractingVisitor::operator()?
Ralf
You are right, thanks! I should have tried to compile that...
paul_71