Virtual function table and virtual destructor in C + +

1. Virtual function table Virtual function ta...
1. Virtual function table

Virtual function table is the basis of realizing polymorphism in C + +. Polymorphism is one of the three characteristics of object-oriented. Polymorphism is conducive to improving the readability of code and facilitating the expansion and maintenance of later code. We all know that the implementation of polymorphism is based on virtual function table. When was the virtual function table created? How does the virtual function table realize the function of polymorphism?

First of all, polymorphism, also known as dynamic polymorphism, determines the function address when the program is running, that is, when the program is running, if the class member function adds the virtual keyword, a virtual function pointer (vfptr) will be established. The pointer points to a virtual function table, which stores the address of the virtual function, The subclass inherits from the parent class and naturally inherits the virtual function pointer. When the subclass rewrites the virtual function of the parent class, the virtual function address in the virtual function table pointed to by the virtual function pointer will be overwritten and replaced with the virtual function address of the subclass. That is, the virtual function address of the subclass is found through the virtual function pointer of the parent class, and then the function is executed. Let's give a detailed description through the code below:

#include <iostream> using namespace std; class Base{ public: void func(){ cout << "Base func" << endl; } }; class Son: public Base{ void func(){ cout << "Son func" << endl; } }; void test(Base& base) { base.func(); } int main () { Son son; cout << "sizeof(Base) = " << sizeof(Base) << endl; cout << "sizeof(Son) = " << sizeof(Son) << endl; test(son); system("pause"); return 0; }

The running result of the code is:

  Because the function members do not occupy the size of the class, the output size of the Base class and Son class is a byte. This byte is used to instantiate the class, reference the derived class by referring to the Base class, and call the func function. The function calls the func of the Base class. This is not the case if we add the virtual keyword.

#include <iostream> using namespace std; class Base{ public: virtual void func(){ cout << "Base func" << endl; } }; class Son: public Base{ void func(){ cout << "Son func" << endl; } }; void test(Base& base) { base.func(); } int main () { Son son; cout << "sizeof(Base) = " << sizeof(Base) << endl; cout << "sizeof(Son) = " << sizeof(Son) << endl; test(son); system("pause"); return 0; }

The running result of the code is:

  It can be seen that after adding the virtual keyword, the size of the parent and child classes becomes four bytes. This is because the virtual function pointer is generated, and the pointer points to the virtual function table. The virtual function table stores the virtual function address. The child classes that inherit the parent class rewrite the virtual function, and the function address in the virtual function table is replaced. Calling the virtual function again is to call the function func of the child class.

2. Virtual deconstruction

Virtual destructor is mainly used to solve the problem that some attributes in the subclass are opened to the heap area. When the parent class pointer calls the function, it cannot call the destructor code of the subclass, resulting in the failure to release the memory in the heap area of the subclass.

First, let's take a look at the memory development of the subclass heap. Call functions through the parent class pointer, capture their constructors and destructors, and see the running results:

#include <iostream> using namespace std; class Base{ public: Base(){ cout << "Base Constructor call for" << endl; } ~Base(){ cout << "Base Destructor call for" << endl; } virtual void func(){ cout << "Base func" << endl; } }; class Son: public Base{ public: Son(int val):m_val(new int (val)) { cout << "Son Constructor call for" << endl; } ~Son(){ cout << "Son Destructor call for" << endl; if (m_val != NULL) { delete m_val; cout << "Son Heap memory free for destructor" << endl; m_val = NULL; } } void func(){ cout << "Son func" << endl; } void funcTest(){ cout << "funcTest function call" << endl; } int* m_val = NULL; }; void test() { Base *base = new Son(10); base->func(); //base->funcTest(); //Cannot be called because the address of this function cannot be found in the virtual function table delete base; base = NULL; } int main () { test(); system("pause"); return 0; }

The running result of the code is:

  It is clear that when a function is called through the parent class pointer, the destructor of the Son class cannot be called, and the memory applied by the Son class on the heap cannot be released, resulting in memory leakage. The main reason why the destructor of Son class cannot be called is that the destructor address of Son cannot be found in the virtual function table. The solution is to write the destructor of Base class as a virtual or pure virtual destructor. The code and operation results of Base class as a pure virtual destructor are given below:

#include <iostream> using namespace std; class Base{ public: Base(){ cout << "Base Constructor call for" << endl; } virtual ~Base() = 0; virtual void func(){ cout << "Base func" << endl; } }; Base :: ~Base(){ cout << "Base Destructor call for" << endl; } class Son: public Base{ public: Son(int val):m_val(new int (val)) { cout << "Son Constructor call for" << endl; } ~Son(){ cout << "Son Destructor call for" << endl; if (m_val != NULL) { delete m_val; cout << "Son Heap memory free for destructor" << endl; m_val = NULL; } } void func(){ cout << "Son func" << endl; } void funcTest(){ cout << "funcTest function call" << endl; } int* m_val = NULL; }; void test() { Base *base = new Son(10); base->func(); //base->funcTest(); //Cannot be called because the address of this function cannot be found in the virtual function table delete base; base = NULL; } int main () { test(); system("pause"); return 0; }

The running result of the code is:

  You can see that as long as the destructor of the Base class is written as a virtual destructor or a pure virtual destructor, and the function is called through the parent class pointer, the destructor code of the subclass will be called, and the memory in the subclass heap will be released.

3 November 2021, 14:16 | Views: 9404

Add new comment

For adding a comment, please log in
or create account

0 comments