You can handcode vtables in C, just as you can handcode loops in assembly (i.e. it works but it's verbose, not particularly readable, and brings more footguns).
But why would you do that if you have an instrument that lets you work at the same level as C, but with methods provided as a proper abstraction that maps exactly to what you'd have written yourself anyway?
I don't know, I never found the "proper abstraction" be more than irrelevant syntactic sugar. And the cost of C++ is that you end up putting everything in the header (IMHO the biggest design flaw of the language) and then compile time start to get long....
At the very least, the proper abstraction is the one that guarantees that you pass the same pointer as the first argument of the method call as the one you used to read the vtable from.
And no, you don't have to put everything in the header with C++, and especially not if you're using it in "C with classes" mode. C++ only needs it for templates - and even then only if you want to use implicit instantiation, with `extern template` available for fine-grained control allowing you to have those specializations separately compiled and implementations kept out of the header file.
But why would you do that if you have an instrument that lets you work at the same level as C, but with methods provided as a proper abstraction that maps exactly to what you'd have written yourself anyway?