Let me give you an example of the differences because they are NOT the same. Yes, dynamic dispatch lets you choose the correct method when you are referring to an object by a superclass, but that magic is very specific to that class hierarchy, and you have to do some declarations in the base class to make it work (abstract methods fill out the vtables since the index of the method in the table cant change between specific types). So, you can call methods in Tabby and Lion and Tiger all by a generic Cat pointer and even have arrays of Cats filled with Lions and Tigers and Tabbys. It knows what indexes those methods refer to in the object's vtable at compile-time (static/early binding), even though the method is selected at run-time (dynamic dispatch).
Now, lets implement an array that contains Lions and Tigers and Bears! ((Oh My!)). Assuming we don't have a base class called Animal, in C++, you are going to have significant work to do to because the compiler isn't going to let you do any dynamic dispatch without a common base class. The indexes for the vtables need to match up, and that can't be done between unreleated classes. You'd need to have a vtable big enough to hold the virtual methods of all classes in the system. C++ programmers rarely see this as a limitation because you have been trained to think a certain way about class design. I'm not saying its better or worse.
With late binding, the run-time takes care of this without a common base class. There is normally a hash table system used to find methods in the classes with a cache system used in the dispatcher. Where in C++, the compiler knows all the types. In a late-bound language, the objects themselves know their type (its not typeless, the objects themselves know exactly who they are in most cases). This means I can have arrays of multiple types of objects if I want (Lions and Tigers and Bears). And you can implement message forwarding and prototyping (allows behaviors to be changed per object without changing the class) and all sorts of other things in ways that are much more flexible and lead to less code overhead than in languages that don't support late binding.
Ever program in Android and use findViewById()? You almost always end up casting the result to get the right type, and casting is basically lying to the compiler and giving up all the static type-checking goodness that is supposed to make static languages superior. Of course, you could instead have findTextViewById(), findEditTextById(), and a million others so that your return types match, but that is throwing polymorphism out the window; arguably the whole basis of OOP. A late-bound language would probably let you simply index by an ID, and treat it like a hash table and not care what the type was being indexed nor returned.
Here's another example. Let's say that you have your Lion class and its default behavior is to eat you when you see it. In C++, if you wanted to have a single "trained" lion, you need to make a new subclass. Prototyping would let you simply change the one or two methods of that particular Lion that need to be changed. It's class and type don't change. C++ can't do that. This is important since when you have a new "AfricanSpottedLion" that inherits from Lion, you can train it too. The prototyping doesn't change the class structure so it can be expanded. This is normally how these languages handle issues that normally require multiple inheritance, or perhaps multiple inheritance is how you handle a lack of prototyping.
FYI, Objective-C is C with SmallTalk's message passing added and SmallTalk is the original OOP, and both are late bound with all the features above and more. Late bound languages may be slightly slower from a micro-level standpoint, but can often allow the code to structured in a way that is more efficient at a macro-level, and it all boils down to preference.