Loay
Loay

Reputation: 601

Why does an abstract class have a vtable?

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?

Upvotes: 6

Views: 2005

Answers (3)

rustyx
rustyx

Reputation: 85531

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.

Upvotes: 3

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275878

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.

Upvotes: 0

dascandy
dascandy

Reputation: 7302

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.

Upvotes: -1

Related Questions