Overriding functions and calling their parent implementations are such fundamental concepts in inheritance that we hardly ever even think about them twice. And we definitely never anticipate any strange behavior!
Let's take a simple example: create a C++ Actor class and then inherit a blueprint class from it. Say, we override the BeginPlay() function in C++ to do something. And in the blueprint we may implement something like this:
Any reasonable developer would expect the execution order to be First thing → Parent implementation → Second thing. However, the actual execution order will most likely be First thing → Second thing → Parent implementation (though it could also be Parent implementation → First thing → Second thing). Or, if you forgot the call to Super::BeginPlay() in your native class, neither First thing nor Second thing would be executed. If you don't believe me, try it!
The key is that the BeginPlay() in blueprints is actually not an override of BeginPlay() in native!
One way to see this for yourself is to observe that the BeginPlay() function in the native Actor class has the following comment above it:
However, in the child blueprint class of this actor, the BeginPlay() node has a similar but different tooltip:
Where does this text come from? Through the wonders of modern technology we can search the tool tip text in our IDE of choice and find out that the function in blueprints is actually ReceiveBeginPlay() defined in Actor.h:
This function is called inside the normal AActor::BeginPlay() just before setting the actor as HasBegunPlay:
At first glance one would expect the blueprint implementation of BeginPlay() to override the native parent implementation. However, we now know that is impossible as they are two different functions. If your design requires a way to override native functionality you will have to create a custom function and use that in blueprints instead.
This also means that one can control the order in which the code is executed by being mindful of where the call to super is placed in the native implementation. To illustrate this point, the following code
with a print string on BeginPlay() in a child blueprint yields the following log output:
Note that without the call to super, the blueprint function will not execute at all!
Finally, you can still add a call to parent by right-clicking the event in blueprints and selecting Add Call to Parent Function; however, if the blueprint is a direct child of a native parent this call will do nothing as there is no parent implementation. If you inherit another blueprint class from the first one, you will see the call to parent is added automatically, since ReceiveBeginPlay() can now be meaningfully overriden. But again, note that you will only be overriding the parent blueprint behavior, and none of the native parent behavior.
Above, I only talk about BeginPlay() to keep the explanation simpler. However, it isn't the only Actor function likely to cause headaches and bugs if one doesn't anticipate the blueprint and native functions to not match up. Here's a (hopefully) complete list of these rascals:
If you comb through Actor.h you will notice over a dozen other BlueprintImplementableEvent functions that start with "Receive" and have user-facing names without it. However, they don't trick programmers into the same trap as their C++ counterparts also have different names.
PS I haven't checked it for sure, but I suspect this article is also relevant for some functions in ActorComponent as well, notably BeginPlay() and EndPlay().