To be more clear, when no "virtual" method is declared, then no table is generated because none is needed.
In this case, the addresses of all the methods are known at compile time and the compiler knows which to choose when anyone is invoked in the source code, based on the type of the object and on the types of the method arguments.
When there is at least one "virtual" method and you have a pointer to an object, the type of the object cannot be known at compile-time, so the compiler cannot determine which method to invoke.
That is why the compiler needs to create a table with pointers to the virtual methods for each class with such methods, and each object of those classes must include a pointer to the corresponding vtable, so that the correct method to invoke can be determined at run time (from the index of the virtual method).
In this case, the addresses of all the methods are known at compile time and the compiler knows which to choose when anyone is invoked in the source code, based on the type of the object and on the types of the method arguments.
When there is at least one "virtual" method and you have a pointer to an object, the type of the object cannot be known at compile-time, so the compiler cannot determine which method to invoke.
That is why the compiler needs to create a table with pointers to the virtual methods for each class with such methods, and each object of those classes must include a pointer to the corresponding vtable, so that the correct method to invoke can be determined at run time (from the index of the virtual method).