The third major feature of C++: polymorphism (1)

Table of contents

   1. The meaning of polymorphism

1. Ordinary call:

2. Polymorphic calling 

        Rewrite function:

Three conditions for realizing polymorphic calling: (one is indispensable)

        Situation 1: When only a virtual function exists in the parent class and neither subclass has a virtual function formed by virtual, polymorphism can also be formed!

        Special case 2: covariance - polymorphism can also be formed


   1. The meaning of polymorphism

        Polymorphic! Generally speaking, it is a variety of forms, specifically to complete a certain behavior, that is, when different objects complete a certain behavior, different states will be produced. If you want to embody the characteristics of polymorphism, you need to use inheritance characteristics!


        To give an example: Take the act of buying a ticket, for example, when ordinary people buy tickets, they pay full price; when students buy tickets, they buy tickets at half price; when soldiers buy tickets, they buy tickets first.

        If you want to achieve different states when various objects do something, there are two ways, one is ordinary calling, and the other is polymorphic calling.

1. Ordinary call:

class Person {
public:
	 void Func() {
		cout << "Person买票——全价" << endl;
	}
};

//子类1:
class Student :public Person{
public:
	 void Func() {
		cout << "Student买票——半价" << endl;
	}
};

//子类2:
class Soldier :public Person {
public:
	 void Func() {
		cout << "Soldier买票——免费" << endl;
	}
};

int main() {
	Person p1;
	Student st1;
	Soldier so1;

	//下面为普通调用,都是各类调用各类的函数
	p1.Func();
	st1.Func();
	so1.Func();
	return 0;
}

operation result: 

2. Polymorphic calling 

class Person {
public:
	void virtual  Func() {
		cout << "Person买票——全价" << endl;
	}
};

class Student :public Person {
public:
	void virtual  Func() {
		cout << "Student买票——半价" << endl;
	}
};

class Soldier :public Person {
public:
	void virtual  Func() {
		cout << "Soldier买票——免费" << endl;
	}
};

void Fun1(Person& p) {
	p.Func();
}

void Fun2(Person* p) {
	p->Func();
}

int main() {
	Person p1;
	Student st1;
	Soldier so1;

	//多态调用
	Fun1(p1);
	Fun1(st1);
	Fun1(so1);

	//多态调用
	Fun2(&p1);
	Fun2(&st1);
	Fun2(&so1);

	return 0;
}

        We found that in the code above, the virtual keyword is added to the member functions of the parent class and child class with the same name. In the process of learning multiple inheritance, we learned that the usage of the virtual keyword is to resolve the ambiguity of diamond inheritance. Sexual issues. The virtual we saw today has a new role: virtual function. As long as virtual is added to the class, it means that the function is a virtual function. 

        The role of virtual functions is to implement polymorphic features.

Rewrite function:

        Overriding functions must be based on the premise of inheritance. Virtual functions whose parent class and subclasses have the same name, the same parameters, and the same function return value are called overriding functions. There is a virtual function in the subclass that is exactly the same as the parent class (that is, the virtual function of the subclass is exactly the same as the virtual function of the parent class (the return value type, function name, and parameter list are exactly the same)). It is said that the virtual function of the subclass overrides the parent class. virtual function.

        In the above code, the Func function in the subclass is an overridden function (the Func function of the parent class is rewritten). Rewriting, literally means rewriting the Func function inherited from the parent class, which means that the content in the Func function body of the subclass is different from the Func function of the parent class. This is to achieve polymorphic features. an important factor.

        Continuing back to the code analysis, in the above test code, three types of objects are created, and then a Fun1 function whose formal parameter is a reference to the parent class is called with different actual parameters. By calling the same function with different calling parameters, it is achieved The results are also different - this is the nature of polymorphism.

        Why does calling the same function with different parameters result in different final results?

        There are two reasons: Reason 1 lies in the formal parameters of the Func function: Person& p This is a reference to the parent class! !

        When I talked about inheritance before, I mentioned that a subclass object can be upconverted and assigned to a parent class object. C++ inheritance - the assignment operation of a subclass object to a parent class object. The reason why subclass objects can be upward converted and assigned to parent class objects is because of slicing. The subclass object contains the parent class member variables inherited from the parent class. When the subclass object is converted upwards, the member variables inherited from the parent class are cut out. The subclass object can assign the cut member variables to the parent class object - completing the operation of cutting, converting and assigning the subclass object to the parent class object. Because of person& p, in main, the Person class object can be used as an actual parameter, or You can use objects of the subclass Student/Soldier as actual parameters to finally complete polymorphism.

 Different implementations of parameters lead to different forms of generation.

Three conditions for realizing polymorphic calling: (one is indispensable)


       1. Use the virtual keyword to modify a function with the same name of the parent and child classes as a virtual function
       2. The virtual function needs to implement three identicals (same name, same parameters, same return value) - rewrite the function
       3. Polymorphic calls need to use parent class references /pointer 

But in the process of writing code, we discovered:

        Situation 1: When only a virtual function exists in the parent class and neither subclass has a virtual function formed by virtual, polymorphism can also be formed!
class Person {
public:
	void Func() {
		cout << "Person买票——全价" << endl;
	}
};

class Student :public Person {
public:
	void Func() {
		cout << "Student买票——半价" << endl;
	}
};

class Soldier :public Person {
public:
	void Func() {
		cout << "Soldier买票——免费" << endl;
	}
};

        For functions without virtual in subclasses, polymorphic features can also be implemented when running test cases. 

         As can be seen from the above, only when there are virtual functions in the parent class Person, the final call to Fun1 can also complete polymorphism. This is because the subclass Student/soldier inherits the members of the parent class, which also includes the member functions of the parent class, so even if the child class It is not necessary to write a virtual function with the same name and type return value in the class, but there are inherited member functions.

          
        But when there is no virtual in the parent class, and although there is virtual in the subclass Student, it cannot form polymorphism. You can try it, and the result will be the same three sentences, all calling the Person's Func function.


        Special case 2: covariance - polymorphism can also be formed

Covariance: In three identical functions, the return value can be different, but the return value must be a pointer or reference of a parent-child class relationship.

code show as below:

class Person {
public:
	virtual Person*   Func() {
		cout << "Person买票——全价" << endl;
		return nullptr;
	}
};

class Student :public Person {
public:
	virtual Student*  Func() {
		cout << "Student买票——半价" << endl;
		return nullptr;
	}
};

class Soldier :public Person {
public:
	virtual Soldier*  Func() {
		cout << "Soldier买票——免费" << endl;
		return nullptr;
	}
};
  

The other code remains unchanged and the test case is run:

         As can be seen from the code above: the return value of the virtual function of the parent class becomes Person*, and the return value of the virtual function of the subclass is Student* and Soldier* respectively, which still forms polymorphism.

        For violations of covariance, the compiler will report a compilation error! as follows:


 


2. Add virtual to the parent class destructor

class Person {
public:
	virtual Person*  Buy_Ticket() {
		cout << "普通人买票——全价" << endl;
		return nullptr;
	}

	~Person() {
		cout << "Person析构 " << endl;
		cout << endl;
		delete[] _i;
	}

private:
	int* _i = new int[10];
};


class Student :public Person {
public:
	 Student*  Buy_Ticket() {
		cout << "学生买票——半价" << endl;
		return nullptr;
	}

	 ~Student() {
		 cout << "Student析构 " << endl;
		 delete[] _s;

	 }
private:
	int* _s = new int[20];
};


class Soldier :public Person {
public:
	 Soldier*  Buy_Ticket() {
		cout << "军人买票——优先" << endl;
		return nullptr;
	}

	 ~Soldier() {
		 cout << "Soldier析构 " << endl;
		 delete[] _sd;
	 }
private:
	int* _sd = new int[20];
};


void Test3() {
	//Person p1;
	//Student st1;
	//Soldier sd1;
	//子类在作用域结束时,编译器会自动调用析构函数
	//先析构子类对象,再调用父类的析构

	cout << endl;


	//通过父类指针,去指向子类对象——创建子类的堆区空间
	Person* pp1 = new Person;
	Person* stt1 = new Student;
	Person* sdd1 = new Soldier;

	delete pp1;
	delete stt1;
	delete sdd1;
}

int main() {
	Test3();
	return 0;
}

By running the code of the Test3() function, we can know:

 

        After deleting these three pointer objects, looking at the results, these three pointers only called the parent class's Person destructor, causing a memory leak! ! !
        Although stt1 is a parent class pointer, it points to a subclass object, and the destructor of the subclass needs to be called.
        Therefore, the above code caused a memory leak. The correction plan is to make it meet the polymorphic conditions and call the destructor of the subclass!

solution:

class Person {
public:
	virtual Person*  Buy_Ticket() {
		cout << "普通人买票——全价" << endl;
		return nullptr;
	}

	//~Person() {
	//	cout << "Person析构 " << endl;
	//	cout << endl;
	//	delete[] _i;
	//}

	//给父类的析构函数无脑+ virtual
	virtual ~Person() {

		cout << "Person析构 " << endl;
		cout << endl;
		delete[] _i;
	}

private:
	int* _i = new int[10];
};

As long as the destructor of the parent class becomes a virtual function, the subclass will have polymorphic characteristics when overriding it. Then after the parent class pointer is destructed after pointing to the subclass object, the destructor of the subclass will also be called. ! 

        Someone wants to ask: Doesn't the destruction of the subclass and the destruction of the parent class need to be the same to satisfy the conditions of polymorphism?

        

        They are three things in common. On the surface, the destructor of a subclass and the destructor of a parent class have the same function parameters and the same return value type (void); in fact, on a deeper level, their function names are also the same, and the bottom layer is called destructor - so it is consistent Three conditions for rewriting

 Correction result:

Summary: In the future, the destructor for the parent class will be brainless + virtual to prevent memory leaks.

Guess you like

Origin blog.csdn.net/weixin_69283129/article/details/132026085