Избегайте использования dynamic_cast

dynamic_cast<> - дорогостоящая операция. Если множественное наследование не используется, для приведения вниз по иерархии наследования (от базового класса к производному) по возможности применяйте static_cast<>. Для приведения к базовому классу dynamic_cast не нужен; static_cast тоже не нужен - обычно достаточно неявного преобразования.

Пример для:

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

struct Derived : Base {
    void foo() override;
};

void bar() {

}

Неправильно:

Base* b = dynamic_cast<Base*>(d); // лишнее

Правильно:

Base* b = d; // неявное преобразование вверх

В большинстве случаев, компилятор способен выявлять ошибочное применение dynamic_cast и оптимизирует заменой на static_cast, однако полагаться на ум и смекалку компилятора не следует.

В случае, когда нельзя гарантировать производный тип, рассмотрите вариант с дополнительным полем в базовом классе, например enum, которое сообщает фактический тип экземпляра.

Пример:

enum class Kind { Derived, Other };

struct Base {
    Kind kind;
    virtual ~Base() = default;
    explicit Base(Kind k) : kind(k) {}
};

struct Derived : Base {
    Derived() : Base(Kind::Derived) {}
    void foo();
};

struct Other : Base {
    Other() : Base(Kind::Other) {}
};

void f(Base* b) {
    if (b->kind == Kind::Derived)
        static_cast<Derived*>(b)->foo();
}