When to use dynamic_cast<T>?

100 views Asked by At

I know about dynamic_cast but seriously wondering when to use that? Why not ALWAYS add the appropriate function to the base class and always have access to the function you need?

Does this have any performance aspects?

I wrote a small example to illustrate this. Without the pure virtual function myUniqueFunc() in the base class A, the function in the derived classes can only be called with a dynamic_cast. So I would include ALWAYS the function in the base class...

In addition, a dynamic_cast requires a lot of overhead and thus computing time.


#include <iostream>
#include <vector>

class A
{
public:
    virtual void printSomething() { std::cout << "class A: hello." << std::endl; };
    virtual void myUniqueFunc() = 0 ;
    virtual ~A() {};
};

class B : public A
{
public:
    void printSomething() override { std::cout << "class B: hello." << std::endl; };
    void myUniqueFunc() { std::cout << "class B: Only I can do this..." << std::endl; }
    B() {};
    virtual ~B() {};

};

class C : public A
{
public:
    //void printSomething() override { std::cout << "class C: hello." << std::endl; };
    void myUniqueFunc() { std::cout << "class C: Only I can do this..." << std::endl; }
    C() {};
    ~C() {};
};


int main()
{
    std::vector<std::unique_ptr<A>> myObjects;
    myObjects.emplace_back(std::make_unique<B>());
    myObjects.emplace_back(std::make_unique<C>());
    myObjects.emplace_back(std::make_unique<B>());
    myObjects.emplace_back(std::make_unique<C>());
    myObjects.emplace_back(std::make_unique<B>());
    myObjects.emplace_back(std::make_unique<C>());

    // 1. fast solution via complete virtual functions in base class
    // iterate over all Objects (common functions only
    for (auto& curObjects : myObjects)
    {
        curObjects->printSomething();
        curObjects->myUniqueFunc();
    }

    
    // 2. slow solution via incomplete virtual functions in base class and downcast to derived classes
    // iterate over all Objects (and calls the special func from the derived classes only)
    for (auto& curObjects : myObjects)
    {
        // downcast to the derived classes to get the unique functions
        if (typeid(curObjects) == typeid(B))
        {
            B& aBClass = dynamic_cast<B&>(*curObjects);
            aBClass.myUniqueFunc();
        }
            
        if (typeid(curObjects) == typeid(C))
        {
            // downcast to the derived classes to get the unique functions
            C& aCClass = dynamic_cast<C&>(*curObjects);
            aCClass.myUniqueFunc();
        }
        
    }

    return 0;
}

3

There are 3 answers

2
Jarod42 On

dynamic_cast allows casting to sibling, for example:

struct IFoo
{
   virtual ~IFoo() = default;
   virtual void foo() = 0;
};

struct IBar
{
   virtual ~IBar() = default;
   virtual void bar() = 0;
   void fooBar() {
       if (auto* foo = dynamic_cast<IFoo*>(this)) {
           foo->foo();
       }
       bar();
   }
};

struct D1 : IBar { /*...*/};
struct D2 : IFoo, IBar { /*...*/ };

Demo

0
Daniel On

Dynamic cast is very popular in engines that handle a lot of different objects, for example in html engines.
Given the following code:

if(auto audio = dynamic_cast<HTMLAudioElement *>(element); audio)
    audio->Play();

Although you can add Play as a virtual function of HTMLElement, there are so many types of elements with their own functions that you will end up with a huge HTMLElement source file.

0
HolyBlackCat On

The base class might come from a library that you can't change.

For example, we make a CAD at work, but the base class for our scene objects comes from a framework that we don't control. And our CAD is medical, while the framework has nothing to do with medicine, so none of our medical functions would make sense in there.

This also makes your code more modular. You can have a module (perhaps a plugin loaded at runtime) that introduces a new derived class along with code for processing it. It's nice to be able to add/remove this module without touching the base classes.