Why is a virtual function table pointer (vfptr) not static in C ++?

If the table of virtual functions is the same for all objects of the class, then why the pointer to this table (vfptr) cannot be static and be common to all objects?

+5
source share
4 answers

vtable is essentially static. But you need the vptr element, which is inside the object for virtual sending and other RTTI operations.

In the vptr implementation, this C ++ code:

class Base { public: virtual void f(); }; class Derived : public Base { public: virtual void f(); }; 

can act similarly like this:

 class Base { public: Base::Base() : m_vptr(&Base_vtab) {} Base::Base(const Base& b) : m_vptr(&Base_vtab) {} void f() { (m_vptr->f_impl)(this); } protected: struct VTable { void (*f_impl)(Base* self); }; const VTable* m_vptr; static const VTable Base_vtab; private: static void Base_f(Base* self); }; const Base::VTable Base::Base_vtab = { &Base::Base_f }; class Derived : public Base { public: Derived::Derived() : Base() { m_vtpr = &Derived_vtab; } Derived::Derived(const Derived& d) : Base(d) { m_vptr = &Derived_vtab; } Derived::~Derived() { m_vptr = &Base::Base_vtab; } protected: static const VTable Derived_vtab; private: static void Derived_f(Derived* self); static void Derived_f(Base* self) { Derived_f(static_cast<Derived*>(self)); } }; const Base::VTable Derived::Derived_vtab = { &Derived::Derived_f }; 
+3
source

The table of virtual functions [provided that the C ++ compiler implements dynamic distribution] is used for all objects of the class. However, each object must know which virtual function table is related to this object. This is what the "virtual function table pointer" points to.

The main idea is that the static type of the link or the pointer to the object tells the compiler what the part of the virtual function table looks like. When he needs to do a virtual dispatch, he simply follows this pointer and decides which function to call. Suppose you have a base class B and derived classes D1 and D2 as follows:

 #include <iostream> struct B { virtual ~B() {} virtual void f() = 0; }; struct D1: public B { void f() override { std::cout << "D1::f()\n"; } }; struct D2: public B { void f() override { std::cout << "D2::f()\n"; } }; 

The virtual function table for D1 and D2 will contain a suitable pointer to D1::f() and D2::f() respectively. When the compiler sees a call through a pointer or a reference to B before f() , it needs to decide at runtime which function to call:

 void g(B* base) { base->f(); } 

To resolve the call, he looks where the pointer to the virtual function is and calls the function n in the corresponding slot (more or less, the contents of the virtual function table tend to be loud, which may also require correction of the pointer).

+2
source

"Virtual" means "determined at runtime." "Static" means "defined during translation."

To make decisions at run time, you must have a parameter (e.g. vptr) that can be set dynamically at run time. That is, for a given reference of the base object x we need some value " x.vptr ", which contains dynamic information (namely information about the most derived class, of which x is the base subobject).

+1
source
 class A { public: virtual void Test(); ... }; class B: public A { public: virtual void Test(); ... } 

if vfptr is static for all objects when compiling the code below:

 void DoTest(A* pA) { ... } A* pA = new B; DoTest(pA); 

A :: vfptr will be recognized and used by the compiler, but this is unexpected!

0
source

Source: https://habr.com/ru/post/1200979/


All Articles