I think there are two cases where using dynamic_cast is a valid thing to do. The first is to check if an object supports an interface, and the second is to break encapsulation. Let me explain both in detail.
Checking for an Interface
Consider the following function:
void DoStuffToObject( Object * obj )
{
ITransactionControl * transaction = dynamic_cast<ITransactionControl>( obj );
if( transaction )
transaction->Begin();
obj->DoStuff();
if( transaction )
transaction->Commit();
}
(ITransactionControl would be a pure abstract class.) In this function, we want to "DoStuff" in the context of a transaction if the object supports transaction semantics. If it doesn't, it's fine to just go ahead anyway.
Now we certainly could just add virtual Begin() and Commit() methods to the Object class, but then every class that derives from Object gets Begin() and Commit() methods, even if they have no awareness of transactions. Use of virtual methods in the base class simply pollutes its interface in this case. The example above promotes better adherance to both the single responsibility principle and the interface segregation principle.
Breaking Encapsulation
This may seem like strange advice considering that dynamic_cast is generally considered harmful because it allows you to break encapsulation. However, done correctly, this can be a perfectly safe and powerful technique. Consider the following function:
std::vector<int> CopyElements( IIterator * iterator )
{
std::vector<int> result;
while( iterator->MoveNext() )
result.push_back( iterator->GetCurrent() );
return result;
}
There's nothing wrong here. But now suppose that you start seeing performance problems in the field. After analyzing, you find that your program is spending an auwful lot of time inside this function. The push_backs result in multiple memory allocations. Even worse, it turns out that "iterator" is almost always an "ArrayIterator". If only you were able to make that assumption, then your performance problems would disappear. With dynamic_cast, you can do exactly that:
std::vector<int> CopyElements( IIterator * iterator )
{
ArrayIterator * arrayIterator = dynamic_cast<ArrayIterator *>( iterator );
if( arrayIterator ) {
return std::vector<int>( arrayIterator->Begin(), arrayIterator->End() );
} else {
std::vector<int> result;
while( iterator->MoveNext() )
result.push_back( iterator->GetCurrent() );
return result;
}
}
Once again, we could add a virtual "CopyElements" method to the IIterator class, but this has the same drawbacks I mentioned above. Namely, it bloats the interface. It forces all implementors to have a CopyElements method, even though ArrayIterator is the only class that will do something interesting in it.
All that being said, I recommend using these techniques sparingly. dynamic_cast is not free and is open to abuse. (And frankly, I've seen it abused far more often than I've seen it used well.) If you find yourself using it a lot, it's a good idea to consider other approaches.