vtables are implementation issues in C++, they are not part of the standard.
vtables are used for both dynamic dispatching of methods and for RTTI. While a nullptr vtable pointer would work for dynamic dispatching (as the vtable pointer is only used when you have an instance of that type) in a pure-abstract class, a dynamic_cast
to a pure abstract class is legal, and it may require that the vtable itself exist.
Designers of the C++ implementation and ABI might have simply given the purely abstract class (a class with no implemented methods, just =0
ones) a vtable to make their implementation simpler. Every class has a vtable, and the vtable pointer gets set during construction of that class. Code can then rely on the fact that the vtable pointer exists and does not have to check for null every time. Code doesn't have to ask questions like "is this a purely abstract class".
For a non-pure abstract class (where some methods have implementations but some are pure virtual), during construction/destruction you can have defined (if unexpected) behavior that involves invoking exactly this class's version of a given method, and not the base class method or an inherited method. For this to work, you need to have a vtable set up. With a pure abstract class, there is no defined result of such a call, so the vtable is redundant, but for an abstract class that isn't totally abstract this does not hold.