Virtual Functions in C
- Transfer
Recently I was asked a question: how would I implement the mechanism of virtual functions in C?
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 worked, I thought there should be a way to mimic 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 overridden by the successor class so that it has its own, different, implementation. C ++ uses a mechanism such as a virtual function table
(briefly vtable) in order to support binding at the program execution stage. 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 look at a simple example of using virtual functions in C ++
In the above example, we have a class
Conclusion:
Now let's think about how to implement the concept of virtual functions in C. Knowing that virtual functions are represented as pointers and stored in vtable, and vtable is a static array, we need to create a structure that simulates the ClassA class itself, the virtual function table for ClassA, and implementation of ClassA methods.
In C, there is no pointer
As we can see from the code above, the implementation
As you can see from the code, we also call the function
This is what the function looks like
Conclusion:
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 similar 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 ++
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 worked, I thought there should be a way to mimic 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 overridden by the successor class so that it has its own, different, implementation. C ++ uses a mechanism such as a virtual function table
(briefly vtable) in order to support binding at the program execution stage. 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 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 above example, we have a class
ClassA
that has two methods: get()
and set()
. The method is get()
marked as a virtual function; in a class, ClassB
its implementation is changing. An integer is data
marked with a keyword protected
so that the derived class has 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 in C. Knowing that virtual functions are represented as pointers and stored in vtable, and vtable is a static array, we need to create a structure that simulates the ClassA class itself, the virtual function table for ClassA, and implementation of 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 pointer
this
that points to the caller. I named the parameter like this in order to simulate using a pointer this
in C ++ (in addition, it looks like the way an object method call in C ++ actually works). As we can see from the code above, the implementation
ClassA_get()
calls the function set()
through a pointer from vtable. Now let's look at the implementation of the derived 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 you can see from the code, we also call the function
set()
from the get()
ClassB implementation using vtable, which points to the desired function set()
, and also access the same integer data
through the “inherited” class ClassA. This is what the function looks like
main()
: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 similar 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 ++