views:

1005

answers:

5

I have a base class with several classes extending it. I have some generic library utilities that creates a vector containing pointers to the base class so that any of the subclasses will work. How can I cast all of the elements of the vector to a specific child class?

// A method is called that assumes that a vector containing
// Dogs casted to Animal is passed.
void myDogCallback(vector<Animal*> &animals) {
    // I want to cast all of the elements of animals to
    // be dogs.
    vector<Dog*> dogs = castAsDogs(animals);
}

My naive solution would look something like this:

// A method is called that assumes that a vector containing
// Dogs casted to Animal is passed.
void myDogCallback(vector<Animal*> &animals) {
    // I want to cast all of the elements of animals to
    // be dogs.
    vector<Dog*> dogs;
    vector<Animal*>::iterator iter;
    for ( iter = animals.begin(); iter != animals.end(); ++iter ) {
        dogs.push_back(dynamic_cast<Dog*>(*iter));
    }
}
A: 

Your code as written will put a bunch of null pointers into your dogs vector when the animals vector contains other animal specializations.

vector<Dog*> dogs;
vector<Animal*>::iterator iter;
Dog* dog;

for( iter = animals.begin(); iter != animals.end(); ++iter )
{
  dog = dynamic_cast<Dog*>(*iter);
  if( dog )
  {
    dogs.push_back( dog );
  }
}
ceretullis
A: 

Usually it is not very good to use dynamic_cast for downcasting. You should probably refactor your code so that you do not need to use explicit downcasting.

See CPP FAQ lite for some more information.

UPD Also, see Stroustrup page (search for "Why can't I assign a vector to a vector?")

Glorphindale
+4  A: 
Kirill V. Lyadvinsky
This is still "looking at each element", the flow is just arranged differently.
Tyler McHenry
A: 

There are two options. The simplest is to use something like remove_copy_if. I cannot explain why they call it this, but it copies the elements from one container to another that don't satisfy the predicate. Here's the basic idea (untested):

struct IsDog : unary_function < Animal *, bool > {
  bool operator ()(Animal * animal) const {
    return dynamic_cast <Dog*> (animal);
  }
};

void foo (vector<Animal*> animals) {
  vector<Dog*> dogs;
  std::remove_copy_if (animals.begin ()
    , animals.end ()
    , back_inserter (dogs)
    , std::not1 ( IsDog () ) );  // not1 here negates the result of IsDog!


  // dogs now contains only animals that were dogs

}

I suppose a way to look at remove_copy_if is to think of it as copy_unless.

An alternative approach, if you base your code around iterators only, is to wrap the iterator for vector < Animal* > with one that returns only the dogs from the collection. The key advantage here is that you still only have one container, but of course you pay a bit more since you're algorithm will navigate over the entire collection of animals.

class dog_iterator  // derive from std::iterator probably with bidirectinoal tag
{
private:
  vector<Animals*>::iterator getNextDogIter (vector<Animals*>::iterator iter) {
    while (iter != m_end) {
      if (0 != dynamic_cast<Dog*> (*iter)) {
        break;
      }
      ++iter;
    }
    return iter;
  }

public:
  dog_iterator (vector<Animals*>::iterator iter, vector<Animals*>::iterator end)
  : m_end (end)
  , m_iter (getNextDogIter (iter))
  {
  }

  // ... all of the usual iterator functions

  dog_iterator & operator++ ()
  {
    // check if m_iter already is at end - otherwise:
    m_iter = getNextDogIter (m_iter + 1);
    return *this;
  }
  // ...
};

This is very rough, but I hope it shows you the basic principle.

Richard Corden
A: 

If you are saying that you can guarantee that each element is really a Dog then just static_cast i.e.

void myDogCallback(vector<Animal*> &animals) {

    const vector<Animal*>::size_type numAnimals = animals.size();

    vector<Dog*> dogs;
    dogs.reserve( numAnimals );

    for ( vector<Animal*>::size_type i = 0; i < numAnimals; ++i ) {
        dogs.push_back(static_cast<Dog*>( animals[i] ));
    }
}

I usually always get a knee-jerk reaction from people that this is bad and you should always use dynamic_cast but, in reality, if you can make guarantees about the type then it's perfectly safe and IMO a sensible thing to do.

Also, your guarantee would imply that the new vector has the same size so reserve the same space to avoid doing any allocations in each push_back. As an alternative loop I've used an index just because I always think iterating using an index must be quicker than an iterator but that's probably nonsense :)

Troubadour