Detailed explanation of C++ virtual function, pure virtual function, virtual destructor, pure virtual destructor, dynamic binding and abstract class.

Table of contents

1. The concept of virtual function

2. Definition of virtual function

3. The role of virtual functions

4. The method of realizing polymorphism with virtual functions

5. Dynamic binding and static binding

6. Pure virtual functions and abstract classes

7. Virtual destructor and pure virtual destructor


1. The concept of virtual function

In C++ programs, we often see the keyword virtual to define a function. Here we need to know the concept of a virtual function : a member declared as virtual in a base class and overridden in one or more of its derived classes Functions become virtual functions.

Overriding : (Occurs in inherited classes) The method names, parameter types, and return value types are identical.

2. Definition of virtual function

class Base {		//基类
public:
	virtual void fun();		//格式 virtual +  成员函数说明
};
class Son :Base{		//派生类  格式 class 类名: 基类名
public:
	//对基类的虚函数进行重写
	virtual void fun();		//格式 virtual +  成员函数说明
};

3. The role of virtual functions

In C++, virtual functions are one of the main means to achieve polymorphism . Polymorphism refers to defining different functions with a name that perform different but similar operations, so that functions with different contents can be called with the same function name . In other words, the same interface can be used to access functions with different functions, thereby realizing "one interface, multiple methods".

4. The method of realizing polymorphism with virtual functions

Define a virtual function in the base class, and its derived class inherits the virtual function and rewrites the function. Objects of different derived classes receive the same information, call the same function name, but perform different operations (performed for each The function rewritten in the derived class), so that the polymorphism is realized by using the virtual function.

Detailed code

#include<iostream>
using namespace std;
class Base {		//基类
public:
	virtual void fun()	//格式 virtual +  成员函数说明
	{
		cout << "Base-fun()" << endl;
	}
};
class Son1 :public Base{		//派生类  格式 class 类名: 基类名
public:
	//对基类的虚函数进行重写
	virtual void fun()		//格式 virtual +  成员函数说明
	{
		cout << "Son1-fun()" << endl;
	}
};
class Son2 :public Base {		//派生类  格式 class 类名: 基类名
public:
	//对基类的虚函数进行重写
	virtual void fun()		//格式 virtual +  成员函数说明
	{
		cout << "Son2-fun()" << endl;
	}
};
void test1()
{
	Base* p1 = new Son1;//一个基类指针p1指向新建的派生类Son1对象
	Base* p2 = new Son2;//一个基类指针p2指向新建的派生类Son2对象
	p1->fun();
	p2->fun();
	delete p1; delete p2;
}
int main()
{
	test1();
}

 In this example, the virtual function fun() in Base is inherited by the derived classes Son1 and Son2, and the fun() function is rewritten in the definition of each derived class. Define two base class pointers p1 and p2 in the main function, pointing to objects of different derived classes from new respectively.

Then when we call the fun() function through the base class pointer, the object of which derived class the base class pointer points to will execute the corresponding fun() function under that class. That is, Son1::fun() and Son2::fun() are executed.

In fact, virtual is no longer needed when redefining the virtual function of the base class in the derived class, and readers can practice it by themselves.


5. Dynamic binding and static binding

Understanding the difference between dynamic binding and static binding in C++ can help us better understand polymorphism.

Static binding: The binding is the static type of the object, and a certain feature (such as a function) depends on the static type of the object, which occurs at compile time.
Dynamic binding: what is bound is the dynamic type of the object, and a certain feature (such as a function) depends on the dynamic type of the object, which occurs at runtime.

In C++, dynamic binding occurs when we use a base class reference (pointer) to call a virtual function. The so-called dynamic binding means that at runtime, the virtual function will select the version of the calling function according to the actual type of the bound object. It has been reflected in the above example.

And the dynamic binding will only be performed if the virtual function is called in the way of "pointer -> function ()" or "reference variable . function ()".

Detailed code

(The definitions of the base class and the derived class are the same as those in the previous figure, and will not be repeated here)

void test2()
{
	Base obj_base;
	Son1 obj_son1;
	Base* p_1 = &obj_son1;//指针方式
	Base& p_2 = obj_son1;//引用方式
	obj_base.fun();			//静态绑定:调用对象本身(基类Base对象)的fun()
	obj_son1.fun();			//静态绑定:调用对象本身(派生类Son1对象)的fun()
	p_1->fun();				//动态绑定:调用被引用对象所属类(派生类Son1)的fun()
	p_2.fun();					//动态绑定:调用被引用对象所属类(派生类Son1)的fun()
}
int main()
{
	test2();
}


 

6. Pure virtual functions and abstract classes

In C++, in many cases, the virtual function cannot be meaningfully implemented in the base class, so it is described as a pure virtual function, and its implementation is completed by the derived class of the base class (that is, rewriting the function ). A class with pure virtual functions is called an abstract class .

Definition of pure virtual function

virtual <type> <function name>(<parameter list>) = 0;

class Base {		//基类
public:
	/*虚函数*/
	//virtual void fun()	//格式 virtual +  成员函数说明
	//{
	//	cout << "Base-fun()" << endl;
	//}

	/*纯虚函数*/
	virtual void fun() = 0;
};

 When a pure virtual function appears in the base class, the class is called an abstract class, and the abstract class has a characteristic: the object cannot be instantiated

When the Base class is an abstract class, if we write a line of code Base b; want to create an object of the Base class, the compiler will report an error (VS2019): E0322 Objects of the abstract class type "Base" are not allowed:   

If the pure virtual function is not rewritten in the derived class of the base class, the derived class cannot instantiate the object. Only after the derived class rewrites the pure virtual function, the derived class can instantiate the object.

/*基类Base中有纯虚函数*/
void test3()
{
	Base obj_base2;  //错误  基类是抽象类,不允许使用抽象类类型"Base"的对象

	/*如果派生类Son1中没有对纯虚函数进行重写*/
	Son1 obj_son2;		//错误  Son1是抽象类,不允许使用抽象类类型"Son1"的对象

	/*派生类Son1中有对纯虚函数进行重写*/
	Son1 obj_son3;		//正确  Son1有对纯虚函数进行重写,可以实例化对象
}

 The main difference between a pure virtual function and a virtual function is: whether an object can be instantiated, and the rest of the operations are roughly the same.


7. Virtual destructor and pure virtual destructor

When is virtual destructor needed? Why use virtual destructor?

In fact, we use virtual destructor in C++ to solve the problem that the base class pointer releases the subclass object is not clean

The reason is that the pointer of the base class will not call the destructor in the derived class when it is destructed, resulting in a memory leak if the subclass has heap attributes

The following set of codes are used to analyze

#include<iostream>
using namespace std;
#include<string>
//虚构和纯虚构
class Animal
{
public:
	Animal()
	{
		cout << "Animal构造函数调用" << endl;
	}
	~Animal()//普通析构函数
	{
		cout << "Animal析构函数调用" << endl;
	}

	//virtual ~Animal()//虚析构函数
	//{
	//	cout << "Animal析构函数调用" << endl;
	//}

	virtual void speak() = 0;//当类中有纯虚函数,类为抽象类
};

class Dog :public Animal
{
public:
	Dog() { m_Name = NULL; }
	Dog(string name)
	{
		cout << "Dog构造函数调用" << endl;
		m_Name = new string(name);		//在堆区开辟内存,最后需要回收
	}
	virtual void speak()		//基类虚函数的重写
	{
		cout << *m_Name << "小狗在说话" << endl;
	}
	~Dog()
	{
		if (m_Name != NULL)
			delete m_Name;//在析构函数中回收开辟的内存
		cout << "Dog析构函数调用" << endl;
	}

	string* m_Name;
};

void test01()
{
	Animal* dog = new Dog("PETTER");
	dog->speak();
	delete dog;
}
int main()
{
	test01();
}

Judging from the execution results of the program, we call the constructors of Animal1 and Dog, but we found that the program only executes the destructor of the base class Animal. Obviously, the destructor of our derived class Dog has not been executed . But we have opened up space for m_Name in the heap area in the derived class, and this part of the space can be recovered only after its destructor is executed, otherwise it will cause a memory leak. So how to solve this problem? It's very simple, we only need to add the keyword virtual before the destructor in the base class to make it a virtual destructor, then we find that the program can execute the destructor of the derived class! Successfully reclaimed memory.

change code

    //~Animal()//普通析构函数
	//{
	//    cout << "Animal析构函数调用" << endl;
	//}

virtual ~Animal()//虚析构函数
	{
		cout << "Animal析构函数调用" << endl;
	}

Now we can execute the destructor of the derived class!


From the above we know the relationship between virtual functions and pure virtual functions, so it is not difficult for us to know the definition of pure virtual destructor

Pure virtual destructor syntax: virtual ~class name()=0;
                                class name::~class name(){}

The difference from the pure virtual function above is that a pure virtual function needs to have a declaration, and also needs to have an implementation process. (The virtual function only needs to have a declaration) Otherwise, an error will be reported during the compilation process: unresolved external symbol.

class Animal
{
public:
	virtual ~Animal() = 0;//纯虚析构的声明,必须有实现过程
};
Animal::~Animal()//Animal纯虚析构的实现代码
{  
		cout << "Animal纯虚析构函数调用" << endl;
}

 It is worth mentioning that if the base class contains a pure virtual destructor, then the class is also an abstract class and objects cannot be instantiated.


 

Commonality between virtual destructor and pure virtual destructor

1. Both can solve the parent class pointer to release subclass objects
2. Both require specific function implementation

The difference between virtual destructor and pure virtual destructor:
If it is pure virtual destructor, the class is an abstract class and cannot instantiate objects.

Guess you like

Origin blog.csdn.net/m0_52910424/article/details/121452005