⬆️ ⬇️

Virtual functions in C

Recently I was asked a question: how would I implement the mechanism of virtual functions in the C language?



At first, I had no idea how to do this: after all, C is not an object-oriented programming language, and there is no such thing as inheritance. But since I already had some experience with C, and I knew how virtual functions work, I thought that there must be a way to simulate the behavior of virtual functions using structures (struct).



A brief explanation for those who do not know what virtual functions are:

A virtual function is a function that can be redefined by a descendant class so that it has its own, distinct, implementation. C ++ uses a mechanism such as a virtual function table.

(briefly vtable) in order to support linking at the stage of program execution. A virtual table is a static array that stores for each virtual function a pointer to the closest implementation of this function in the inheritance hierarchy. The closest implementation in the hierarchy is determined at run time by retrieving the function address from the object's method table.

')

Let's now take a look at a simple example of using virtual functions in C ++.



class ClassA { public: ClassA() {data = 10;} virtual void set() { std::cout << "ClassA is increasing" << std::endl; data++; } int get() { set(); return data; } protected: int data; }; class ClassB : public ClassA { public: void set() { std::cout << "ClassB is decreasing" << std::endl; data--; } }; 


In the example above, we have the class ClassA , which has two methods: get() and set() . The get() method is marked as a virtual function; in class ClassB its implementation changes. The integer data marked with the protected keyword so that the class-descendant ClassB access to it.



 int main() { ClassA classA; ClassB classB; std::cout << "ClassA value: " << classA.get() << std::endl; std::cout << "ClassB value: " << classB.get() << std::endl; return 0; } 


Conclusion:



 ClassA is increasing ClassA value: 11 ClassB is decreasing ClassB value: 9 


Now let's think about how to implement the concept of virtual functions on C. Knowing that virtual functions are represented as pointers and stored in a vtable, and vtable is a static array, we have to create a structure that simulates the ClassA class itself, the virtual function table for ClassA, and implementation of the ClassA methods.



 /*    */ struct ClassA; /*  ,    . */ typedef struct { void (*ClassA)(struct ClassA*); /* "" */ void (*set)(struct ClassA*); /*  set */ int (*get)(struct ClassA*); /*  get */ } ClassA_functiontable; typedef struct ClassA { int data; ClassA_functiontable *vtable; /*    ClassA */ } ClassA; /*   ClassA */ void ClassA_constructor(ClassA *this); void ClassA_set(ClassA *this); int ClassA_get(ClassA *this); /*     ClassA */ ClassA_functiontable ClassA_vtable = {ClassA_constructor, ClassA_set, ClassA_get }; /*   */ void ClassA_constructor(ClassA *this) { this->vtable = &ClassA_vtable; this->data = 10; } void ClassA_set(ClassA *this) { printf("ClassA is increasing\n"); this->data++; } int ClassA_get(ClassA *this) { this->vtable->set(this); return this->data; } 


In C, there is no this pointer that points to the caller. I called the parameter so as to mimic the use of the this pointer in C ++ (besides, this is similar to how an object method call actually works in C ++).



As we can see from the code above, the implementation of ClassA_get() calls the set() function via a pointer from the vtable. Now let's look at the implementation of the heir class:



 /*    */ struct ClassB; /*  ,     ,      */ typedef struct { void (*ClassB)(struct ClassB*); void (*set)(struct ClassB*); void (*get)(struct ClassA*); } ClassB_functiontable; typedef struct ClassB { ClassA inherited_class; } ClassB; void ClassB_constructor(ClassB *this); void ClassB_set(ClassB *this); int ClassB_get(ClassB *this); ClassB_functiontable ClassB_vtable = {ClassB_constructor, ClassB_set, ClassB_get}; void ClassB_constructor(ClassB *this) { /*     */ ClassA_constructor((ClassA*)this); /*          */ this->inherited_class.vtable = (ClassA_functiontable*)&ClassB_vtable; } void ClassB_set(ClassB *this) { printf("ClassB decreasing\n"); this->inherited_class.data--; } int ClassB_get(ClassB *this) { this->inherited_class.vtable->set((ClassA*)this); return this->inherited_class.data; } 


As can be seen from the code, we also call the set() function from the implementation of the get() ClassB using vtable, which points to the desired set() function, and also access the same integer data via the “inherited” class ClassA.



This is what main() function looks like:



 int main() { ClassA classA; ClassB classB; ClassA_constructor(&classA); ClassB_constructor(&classB); printf("ClassA value: %d\n", classA.vtable->get(&classA)); /*   get()  - */ printf("ClassB value: %d\n", classB.inherited_class.vtable->get((struct ClassA*)&classB)); } 


Conclusion:



 ClassA is increasing ClassA value: 11 ClassB decreasing ClassB value: 9 


Of course, this trick does not look as natural as in C ++ or in another object-oriented programming language, and I never had to implement something like this when I wrote programs in C, but nevertheless it can help to better understand the internal structure virtual functions.



from the translator: a detailed article about the internal implementation of virtual functions in C ++

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



All Articles