Why does an abstract class have a vtable?
Asked Answered
H

3

6

Regarding this post:

For implementations that use vtable, the answer is: Yes, usually. You might think that vtable isn't required for abstract classes because the derived class will have its own vtable, but it is needed during construction: While the base class is being constructed, it sets the vtable pointer to its own vtable. Later when the derived class constructor is entered, it will use its own vtable instead.

I'm assuming the answer is correct, but I don't quite get it. Why is the vtable needed exactly for construction?

Hypha answered 8/9, 2016 at 10:58 Comment(3)
See #18581279Diminutive
"Yes, usually", and after the quoted text, he shows a directive for visual to remove it.Booze
So that if the constructor calls a virtual method, it calls its own implementation, not a derived class's.Shudder
S
3

Because the standard says so.

[class.cdtor]/4

When a virtual function is called directly or indirectly from a constructor or from a destructor, including during the construction or destruction of the class's non-static data members, and the object to which the call applies is the object (call it x) under construction or destruction, the function called is the final overrider in the constructor's or destructor's class and not one overriding it in a more-derived class.

The rationale is that first the base class is constructed, then the derived one. If a virtual function is called inside the base class' constructor, it would be bad to call the derived class, since the derived class isn't initialized yet.

Remember that an abstract class may have non-pure virtual functions. Also, for debugging purposes, it is good to point pure virtual functions to a debugging trap (e.g. MSVC calls _purecall()).

If all virtual functions are pure, in MSVC you can omit the vtable with __declspec(novtable). If you use a lot of interface classes, this can lead to significant savings because you omit vfptr initialization. But if you accidentally call a pure virtual function, you'll get a hard to debug access violation.

Schaaff answered 8/9, 2016 at 11:29 Comment(0)
F
0

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.

Fun answered 8/9, 2016 at 13:26 Comment(5)
There is no such thing as "pure abstract" in C++.Canaletto
@Canaletto A class can be called abstract if you cannot instantiate it (it has at least one pure virtual function). A class could be called purely abstract if it has nothing but pure virtual functions. None of these are terms in the C++ standard, I will admit, but I don't think their use is that confusing. I have added clarification.Fun
"dynamic_cast to a pure abstract class is legal" could you please provide an example of such a cast?Canaletto
@curious struct A{ virtual void foo()=0; }; struct B { virtual ~B(){}; }; struct C: B,A { void foo()override{}; }; B* b=new C(); A* a= dynamic_cast<A*>(b); a->foo();? Is that code not implied by a purely abstract class, and doing a dynamic cast to it?Fun
The relatively involved code of dynamic_cast will use the type information of the vptr of base class B to find the most derived class C then walk the inheritance tree to find the base class A; together with the inheritance tree, the runtime type information contains the relative offsets of the base class subobjects.Canaletto
P
-1

When your class has a pure virtual function, that does not mean you cannot also have an implementation for it (!!). So that implies you can have an abstract class, which is also fully implemented. The constructor of your abstract class has to be able to call all functions - even the pure virtual ones, because of this point - that exist for it so far.

If you'd have substituted the client one, you'd get different behaviour for the base class constructor depending on the deriving class - not a great idea, so that's not allowed. You could put in place no vtable and statically resolve all function calls - that works, but it implies handling the constructor specially compared to all other functions and requires inlining all other functions to do this (since a function called from the constructor may also call a virtual etc.) - not very practical.

So it just implements a vtable for the constructor and destructor to use during construction and destruction. It allows you to use typeid and dynamic_cast in the c'tor and d'tor with the predictable result and get reliable behaviour out of the virtual functions you have. No alternative solution would do that.

Pageantry answered 8/9, 2016 at 11:17 Comment(2)
I don't think it implies that at all. The pure virtual functions can all have definitions, but that doesn't mean the abstract class is fully implemented. It certainly isn't in any practical sense, because it cannot ever be instantiated so that it could make calls to member functions. The concrete derived class would simply have the responsibility of maintaining the vtable. The real answer is because the standard says this is how it works.Cutworm
Even if you provide a definition for a pure virtual function, that doesn't mean you can virtually call a pure virtual function!Canaletto

© 2022 - 2024 — McMap. All rights reserved.