在面向对象编程中,多态性是一个重要的概念,它体现在具有继承关系的父类和子类之间。**多态性的实现离不开虚函数和纯虚函数的支持。**本文将深入探讨C++中的多态性和纯虚函数,以及虚析构函数的重要性。
一、多态性
多态性体现在子类重新定义(重写)父类的成员函数,并且父类将这个函数声明为虚函数(virtual)。通过父类的指针,在程序运行时期,动态绑定到父类指针上的对象可能是某个子类对象,也可能是父类对象。系统内部会查找虚函数表,从而调用父类或子类的虚函数,实现了运行时期的多态性。
二、纯虚函数
纯虚函数是在基类(父类)中声明的函数,但在基类(父类)中没有定义,要求任何派生类(子类)都要定义该虚函数的实现方法。基类中实现纯虚函数的方法是在函数原型后增加一个 “=0”。
virtual void eat2() = 0;//纯虚函数,没有函数体只有一个函数声明
Human human;//不合法,报错,下面解释
Human* p = new Human;//不合法
**重点:大家注意,一旦在一个类中有纯虚函数了,那么你就不能再生成这个类的对象了。
抽象类,不能用来生成对象,主要目的是用来统一管理子类对象
所有大家记住两点:
(1)函数纯虚函数的类 叫抽象类,不能用来生成该类的对象(Human human),主要用于当做基类生成子类用
(2)子类中必须实现该基类中定义的纯虚函数**
Human* phuman = new Men;
phuman->eat2();
delete phuman;
Human* phuman2 = new Women;
phuman2->eat2();
delete phuman2;
三、虚析构函数
在面向对象编程中,基类对派生类及其对象的操作只能影响到那些从基类继承下来的成员。如果想要用基类对非继承成员进行操作,需要把基类的函数定义为虚函数,包括析构函数。只有基类的析构函数是虚函数,才能保证在delete基类指针时能够运行正确的析构函数版本。
#include <iostream>
using namespace std;
class Human {
public:
virtual void eat() {
cout << "Human is eating" << endl;
}
virtual void eat2() = 0;
virtual ~Human() {
cout << "Human destructor" << endl;
}
};
class Men : public Human {
public:
void eat() override {
cout << "Men is eating" << endl;
}
void eat2() override {
cout << "Men is eating 2" << endl;
}
~Men() {
cout << "Men destructor" << endl;
}
};
class Women : public Human {
public:
void eat() override {
cout << "Women is eating" << endl;
}
void eat2() override {
cout << "Women is eating 2" << endl;
}
~Women() {
cout << "Women destructor" << endl;
}
};
int main() {
Human* phuman = new Men;
phuman->eat(); // Men is eating
phuman->eat2(); // Men is eating
return 0;
}
Men men;
上面一行和下面两行代码执行结果相同
Men* pmen = new Men;
delete pmen;
Human* phuman = new Men;
delete phuman;//没有执行子类的析构函数,只执行了父类Human的析构函数,坏事
四、总结
结论:用基类指针new子类对象在delete的时候系统不会调用子类(派生类)的析构函数,这肯定有问题
如何解决: 把基类(父类~Human())的析构函数也写成虚函数(virtual ~Human())就可以解决问题
解决结论:
(1)在public继承中,基类(父类)对派生类(子类)及其对象的操作,只能影响到那些从基类继承下来的成员,
如果想要用基类(父类)对非继承成员进行操作,则要把基类(父类)的这个函数定义为虚函数,析构函数自然也应该如此
(2)另外就是基类(父类)中的析构函数的虚属性也会被继承给子类,这样的话子类中的析构函数也自然而然成了虚函数
虽然子类的析构函数名字和基类的析构函数名不同,但仍然成了虚函数,
(3)delete phuman的时候,肯定要调用父类的析构函数,但是在父类的析构函数中,它要是想要调用子类Men的析构函数
那么Human这个类中析构函数要声明为virtual,也就是说c++中为了获得运行时的多态行为,所调用的成员函数
必须得是virtual的
(4)如果一个类想要做基类,我们务必要把这个类的析构函数写成virtual析构函数,重中之重,重要的事说三遍
(5)只要基类的析构函数是虚函数,就能够保证我们delete基类指针时能够运行正确的析构函数版本
(6)普通类我们可以不写析构函数,但如果是基类我们就必须要写 虚析构函数 。
(7)虚函数会增加内存开销,类里面定义虚函数,编译器就会增加一个虚函数表,在这个表里面存在虚函数指针。
通过这个示例,我们可以深入理解C++中的多态性和纯虚函数的作用,以及虚析构函数的重要性。在实际开发中,合理使用虚函数和虚析构函数可以更好地实现面向对象的设计和编程。