I heard Abstract base classes and virtual function were dope.
They also have a speed penalty so only use them if you need to.
They should still be learned and used.
Yes, virtual functions have a speed cost. On today's systems in C++ and C# it is around 5ns per virtual call, in Java it is around 40ns in post-JIT code on most VMs.
However, other options to achieve dynamic dispatch also have a cost. Generally those other options have an equal or greater speed cost.
If you need dynamic dispatch, or in other words, if you need a system where you can call a function or a function pointer which can be swapped out based on the nature of the object, then a virtual function is an excellent choice. It is already written, is built in to many languages, and every experienced programmer using it knows what it means.
You are right that you shouldn't use them when they're a bad fit. For example, calling a function on every object regardless of its need for the function, then it is a bad fit. Calling it on an array of 1000 objects but only 3 of them implement the function, that is a waste of 997 function calls; that should probably follow an event registration and listener pattern. But if you really do need dynamic dispatch, and all 1000 objects need it, that is exactly what virtual calls are for and they should be used.