1

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;
}

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
  • https://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used – Motomotes May 13 '23 at 15:15
  • `if (typeid(curObjects) == typeid(C)) { C& aCClass = dynamic_cast(*curObjects);` can be replaced `if (C* c = dynamic_cast(curObjects.get()))`. – Jarod42 May 13 '23 at 15:16
  • [When should static_cast, dynamic_cast, const_cast, and reinterpret_cast be used?](https://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used) – Retired Ninja May 13 '23 at 15:16
  • Not sure than virtual function would be significantly faster than one `dynamic_cast`. – Jarod42 May 13 '23 at 15:17
  • I've worked with code where dynamic casts were used for dependency injection. The dependencies to be injected all had a common base class so we could store unique_ptrs to them in a container, but were otherwise heterogenous so dynamic_casts were used to actually select appropriate ones. – Nathan Pierson May 13 '23 at 15:19
  • Does this answer your question? [When should static\_cast, dynamic\_cast, const\_cast, and reinterpret\_cast be used?](https://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used) – Jørgen Fogh May 13 '23 at 20:27

3 Answers3

1

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

Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

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.

Daniel
  • 30,896
  • 18
  • 85
  • 139
0

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.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207