c + + ~ Lesson 8: virtual functions and polymorphism

Posted by WiseGuy on Sun, 06 Feb 2022 05:35:31 +0100

catalogue

Virtual function and virtual function table

Virtual function and polymorphism

Pure virtual function and ADT

virtual destructor

override and final

c + + type conversion

Virtual function and virtual function table

  • What is a virtual function? Member functions modified with virtual are called virtual functions
  • The influence of virtual functions on classes

Add a pointer memory, 32-bit 4 bytes, 64 bit 8 bytes

  • Virtual function table

Is a pointer that stores the first address of all virtual functions

#include <iostream>
#include <string>
using namespace std;
class A
{
public:
	void print()
	{
		cout << "function" << endl;
	}
	virtual void print2()
	{
		cout << "Virtual function 1" << endl;
	}
	virtual void print3()
	{
		cout << "Virtual function 2" << endl;
	}
	virtual void print4();
};
void A::print4()//Virtual is declared in the class. The implementation outside the class does not need virtual modification, but only needs to be qualified by the class name
{
	cout << "Virtual function 3" << endl;
}
void Size()
{
	//Empty structures are not allowed in C language
	//Ordinary functions in class do not affect memory, while virtual functions do
	//With data, one byte does not exist
	//cout <<sizeof(A) << endl;// Empty classes or structures occupy 1 byte, virtual functions occupy 4 bytes, and multiple virtual functions also occupy 4 bytes

	A a;
	int** vptr = (int**)&a;//Type conversion
	typedef void(*PF)();
	PF func = (PF)vptr[0][0];
	func();//Call virtual function 1
	func = (PF)vptr[0][1];
	func();//Call virtual function 2
	func = (PF)vptr[0][2];
	func();//Call virtual function 3
}
int main()
{
	Size();
	
	return 0;
}

Virtual function and polymorphism

  • Polymorphism definition: different results caused by the same behavior (call)
  • The necessity principle of polymorphism:

① Virtual function must exist in parent class

② Subclasses must adopt public inheritance

③ Pointer or reference must exist (use)

#include <iostream>
#include <string>
using namespace std;
class Man
{
public:
	void WC1()
	{
		cout << "Men go to the bathroom" << endl;
	}
	virtual void WC2()//Parent class must have virtual
	{
		cout << "Dirty men go to the bathroom" << endl;
	}
protected:
	
};

class Woman:public Man
{
public:
	void WC1()
	{
		cout << "Women go to the bathroom" << endl;
	}
	void WC2()
	{
		cout << "use the toilet" << endl;
	}
protected:
};
void test()
{
	//There is no polymorphism in normal access
	cout << "Normal access, proximity principle" << endl;
	Man pm;
	pm.WC1();
	pm.WC2();
	Woman wm;
	wm.WC1();
	wm.WC2();
	cout << endl<<"Pointer access, proximity principle" << endl;
	Man* p1 = new Man;
	p1->WC1();
	p1->WC2();
	Woman* p2 = new Woman;
	p2->WC1();
	p2->WC2();
	cout << endl << "The pointer is assigned abnormally, and the subclass object initializes the parent class pointer"<<endl;
	Man* m1 = new Woman;
	//virtual looks at objects, not pointer types
	m1->WC1();//Ordinary function
	m1->WC2();//Virtual function (calling subclass)
	m1 = new Man;
	m1->WC1();//Calling the parent class
	m1 = new Woman;
	m1->WC2();//Calling subclasses
}
int main()
{
	test();
	
	return 0;
}

  • Static polymorphism: if the function address can be determined at the compilation stage, then static binding
  • Dynamic polymorphism: if the function address can only be determined at the running stage, it is dynamic binding
  • Polymorphism meets the following conditions:

① (2) the subclass overrides the virtual function of the parent class

  • Polymorphic use: the parent class pointer or reference points to the subclass object
  • Rewrite: the return value type of the function is exactly the same as the function name and parameter list
#include <iostream>
#include <string>
using namespace std;

class Shape
{
public:
	virtual void Draw()
	{
		cout << "Drawing process" << endl;
	}
protected:
};
class Rect :public Shape
{
public:
	virtual void Draw()
	{
		cout << "draw rectangle" << endl;
	}
protected:
};
class Circle :public Shape
{
public:
	virtual void Draw()
	{
		cout << "Draw circle" << endl;
	}
protected:
};
//Reduce code changes due to changes
//Meet new requirements by adding code
//Unified interface
class Tool
{
public:
	void draw(Shape* parent)
	{
		parent->Draw();
	}
protected:
};

int main()
{
	Tool* pTool = new Tool;
	pTool->draw(new Rect);
	pTool->draw(new Circle);
	return 0;
}

Pure virtual function and ADT

  • Pure virtual functions are also virtual functions, but pure virtual functions have no function body
virtual void print() = 0;//In the class, the function is written like this
  • Abstract class: a class with at least one pure virtual function is called an abstract class

Abstract classes cannot construct objects; Abstract classes can build object pointers; A derived class cannot instantiate an object after inheritance. Only by overriding a pure virtual function can a derived class instantiate an object.

  • Defining a function as a virtual function does not mean that the function is not implemented.

    It is defined as a virtual function to allow the function of a subclass to be called with a pointer to the base class.

    Defining a function as a pure virtual function means that the function has not been implemented.

    A pure virtual function is defined to implement an interface and play the role of a specification. Programmers who inherit this class must implement this function.

//abstract class
class Parent
{
public:
	virtual void print() = 0;//Cannot write outside class
protected:
};
void test()
{
	//Parent p;// Error (active) 	 E0322 	 Objects of abstract class type 'parent' are not allowed
	Parent* p=nullptr;//But you can build pointers
}
  • Pure virtual functions are not rewritten. They are pure virtual functions no matter how many times they are inherited, and virtual functions are virtual functions no matter how many times they are inherited
#include <iostream>
#include <string>
using namespace std;

//Pure virtual function is the process of ADT(abstract data type)
class stack
{
public:
	//All operations in the parent class are described
	virtual void push(int data) = 0;//Push 
	virtual void pop() = 0;//Out of stack
	virtual int top()const = 0;//
	virtual bool empty()const = 0;
	virtual int size()const = 0;
protected:
};
//If a subclass wants to create an object, it must override the pure virtual function of the parent class
//ADT: is as like as two peas.
class arrayStack :public stack
{
public:
//rewrite
	void push(int data)
	{

	}
	void pop()
	{

	}
	int top()const
	{
		return 1;
	}
	bool empty()const
	{
		return true;
	}
	int size()const
	{
		return 1;
	}
	//Other functions can be added
	//Other members can be added
protected:
	int* array;
};
struct Node
{
	int data;
	Node* next;
};
class listStack :public stack
{
public:
	void push(int data)
	{

	}
	void pop()
	{

	}
	int top()const
	{
		return 1;
	}
	bool empty()const
	{
		return false;
	}
	int size()const
	{
		return 1;
	}
	//Other functions can be added
	//Other members can be added
protected:
	Node* headNode;
};

void test(stack* pstack)
{
	pstack->push(1);
	while (!pstack->empty())
	{
		cout << pstack->top();
		pstack->pop();
	}
}
int main()
{
	test(new arrayStack);
	test(new listStack);

	return 0;
}

virtual destructor

#include <iostream>
#include <string>
using namespace std;

class A
{
public:
	virtual ~A()
	{
		cout << "Parent class destructor" << endl;
	 }
	void print() {}
protected:
	
};
class B :public A
{
public:
	virtual ~B()
	{
		cout << "Subclass deconstruction" << endl;
	}
	void print() {}
protected:
};
int main()
{
	//The parent class needs to be initialized with a pointer, and the parent class needs to be destructed
	A* p = new B;
	p->print();
	delete p;
	return 0;
}

override and final

final: modifies the virtual function, indicating that the virtual function can no longer be rewritten

override: check whether the function overrides a virtual function of the parent class and force it to be overridden; (it has nothing to do with final)

#include <iostream>
#include <string>
using namespace std;

class A
{
public:
	//final: override prohibited
	//A function with the same name cannot exist in a final subclass
	virtual void func()final
	{
		cout << "rewrite" << endl;
	}
	virtual void print(){}
protected:
	
};
class B :public A
{
public:
	//Restricted subclasses cannot write this function
	/*void func()
	{

	}*/
	void print()override//Forced override, which identifies the role and is used to check whether the parent class has the current virtual function
	{
		cout << "override" << endl;
	}
protected:
};
int main()
{
	B b;
	b.func();//Subclasses can call functions, but they cannot be changed
	return 0;
}

c + + type conversion

const_cast: remove the const attribute or add the const attribute

  • const_ Cast < target type > (identifier): the target type can only be pointer or reference
#include <iostream>
#include <string>
using namespace std;
/*
	const_cast<Type to convert > (target to convert)
	1.Remove const attribute (provide a modifiable interface to operate the variable of cinst attribute)
	2.Add const attribute (less used)
*/

class A
{
public:
	A(const char* str) :str(const_cast<char*>(str))//So you can convert
	{
		//this->str = str;// error
		this->str = const_cast<char*>(str);
		cout << str << endl;
	}
protected:
	char* str;
};

int main()
{
	const int num = 9;
	//Remove const attribute
	//int* pnum = &num;// Error a value of type 'const int *' cannot be used to initialize an entity of type 'int *
	//1. Common pointer
	int* pnum = const_cast<int*>(&num);
	*pnum = 88;
	cout << *pnum << endl;
	//2. char * type pointer in operation class
	A a("constant");//Pass constant can
	char pp[] = "variable";
	A a2(pp);//Transmission variable
	//3. Operation constant reference
	const int& pk =7;
	int& ppk = const_cast<int&>(pk);
	cout << ppk << endl;
	//Add const attribute
	int aa = 9;
	const int* pa = &aa;
	const int* paa =const_cast<const int*>(pa);
	return 0;
}

static_cast:

  • static_ Cast < type to convert > (target to convert)
#include <iostream>
#include <string>
using namespace std;
/*
	static_cast<Type to convert > (target to convert)
	1.Basic data type conversion (similar to C language forced type conversion)
	2.Convert null pointer to target type pointer
	3.Convert any type of variable to void type
	4.Transformations used on classes (transformations between base and derived class objects)
		4.1 It is safe to perform uplink conversion (from child to parent pointer or reference conversion)
		4.2 It is not safe to perform a downstream conversion (from parent to child pointer or reference conversion)
	Note: static_cast cannot convert const
*/

class Parent
{
public:
	virtual void print()
	{
		cout << "Parent" << endl;
	}
protected:
	
};

class Son :public Parent
{
public:
	 void print()
	{
		cout << "Son" << endl;
	}
protected:
};

int main()
{
	//1. The basic data type is automatically and implicitly converted whether it is converted or not
	char cNum = 'A';
	int iNum = static_cast<int>(cNum);
	cout << iNum << endl;
	//2. Null type pointer
	double* pd = new double;
	void* pvoid = static_cast<void*>(pd);
	//3.const type conversion
	int x = 0;
	const int constNum =static_cast<const int>(x);
	//4. Incorrect usage
	const int xx = 0;
	//int* p = static_ cast<int*>(&xx);// Const must be used to remove const_cast
	//5. From son to father
	Parent* p1 = new Son;
	p1->print();
	Son* s1 = new Son;
	p1 = static_cast<Parent*>(s1);
	p1->print();
	//6. From father to son
	Parent* parent;
	Son* son;
	//son = parent;// error
	Parent* p2 = new Parent;
	Son* s2 = static_cast<Son*>(p2);//unsafe
	s2->print();

	return 0;
}

 dynamic_cast

  •  dynamic_ Cast < type to convert > (target to convert)
  • dynamic_cast should check whether the pointer after conversion is empty; static_cast doesn't need it, but you should know it well
#include <iostream>
#include <string>
using namespace std;
/*
	dynamic_cast<Type to convert > (target to convert)
	1.Uplink conversion and static_ Same as cast
	2.Downlink conversion dynamic_cast is safer
	3.Cross transform multiple inheritance
*/

class MM
{
public:
	MM(string mmName = "Parent class") :mmName(mmName)
	{
		cout << mmName << endl;
	}
	virtual void print()
	{
		cout << "MM" << endl;
	}
protected:
	string mmName;
	
};

class Son :public MM
{
public:
	Son(string sName="Subclass") :sName(sName)
	{
		cout << sName << endl;
	}
	  void print()
	{
		cout << "Son" << endl;
	}
	 void printData()
	 {
		 cout << "printData" << endl;
	 }
protected:
	string sName;
};

class A
{
public:
	virtual void print()
	{
		cout << "A" << endl;
	}
protected:
};
class B :public A
{
public:
	virtual void print()
	{
		cout << "B" << endl;
	}
protected:
};
class C :public A, public B
{
public:
	virtual void print()
	{
		cout << "C" << endl;
	}
protected:
};


int main()
{
	MM* pM = new MM;
	Son* sS = new Son;
	//1. Uplink conversion
	MM* pSM = static_cast<MM*>(sS);
	MM* pSM2 = dynamic_cast<MM*>(sS);
	pSM->print();
	cout << endl;
	pSM2->print();
	//2. Downlink conversion
	Son* pSS = static_cast<Son*>(pM);//If virtual does not exist, no error will be reported
	//Son* pSS2 = dynamic_ cast<Son*>(pM);// If virtual does not exist, an error will be reported
	pSS->print();//The parent class does not exist virtual, and the child class will be interrupted if virtual exists
	cout << endl;
	//Downstream conversion, calling a function without a parent class in a subclass will report an error
	//pSS2->print();
	
	//3. Cross conversion
	A* a = new C;
	B* b = dynamic_cast<B*>(a);
	b->print();//C
	//C* cc = new B;// error
	return 0;
}

 

reinterpret_cast

  •  reinterpret_ Cast < type to convert > (target to convert)
  • From pointer type to a sufficiently large integer type
  • From integer type or enumeration type to pointer type
  • Auxiliary hash function
#include <iostream>
#include <string>
using namespace std;
int Max(int a, int b)
{
	return a > b ? a : b;
}
int main()
{
	int num = reinterpret_cast<int>(Max);
	cout << num << endl;//Print number: 16322875
	cout << Max << endl;//Print address: 00F9113B
	auto pMax= reinterpret_cast<int(*)(int, int)>(num);//auto pMax is equivalent to int(*pMax)(int,int)
	cout << pMax(3, 8) << endl;
	return 0;
}

Topics: C++ Back-end