多态的概念
多态的表现形式一般就是:
指向的对象不同,调用同名的函数,具体调用到的函数就不同
例
父类A和子类B中都有一个函数func(),构成多态的时候:
A*p=new A; 指向父类对象
p->func(); 就调用父类中的func
A*p=new B; 指向子类对象
p->func(); 就调用子类中的func
多态的实现
多态产生的条件
例
什么是虚函数?
在C++中,虚函数是使用virtual关键字修饰的非静态成员函数。
虚函数的主要作用是允许在派生类中重新定义基类的函数,从而实现多态。
关于虚函数的一些注意点:
-
虚函数就是为了实现多态而存在的,而且支持虚函数是需要付出一定代价的
所以如果不实现多态,就不要定义虚函数
-
静态成员函数不能做虚函数
因为
①虚表指针存在对象里,但是静态成员的生命周期比对象长,而且静态成员函数里面没有this指针,就找不到对象
②因为静态的特性:在以该父类为起始的整个继承体系中只有一份,如果实行多态的话就有多份了,这不符合静态的特性
-
在父类中声明为虚函数的成员函数,继承到子类后,这个成员函数即使没有被virtual修饰也是虚函数。
但是还是建议在子类中也加上virtual修饰,这样代码的可读性更高
虚函数的重写和协变
重写
子类中有与父类完全相同【返回值类型、函数名、参数列表完全相同】的虚函数,称子类的虚函数重写了父类的虚函数。
例
协变
子类和父类的虚函数的返回值可以不同【其他两个(函数名,参数表)依旧必须相同】,但是满足以下3个条件的就构成协变
父类的虚函数的返回值是一个父类类型的指针或者引用子类的虚函数的返回值是一个子类类型的指针或者引用- 子类和父类的
返回值中的子类和父类必须是同一个继承体系的
例
析构函数的重写
重写的要求上面说了,即必须子类和父类的虚函数的返回值,函数名,参数表都相同才可以构成重写
但是析构函数名字的特殊性【~类名】,就让同一作用域中的两个类的析构函数的名字不可能相同,因为这两个类的类名不可能相同
所以析构函数不能构成重写吗?
并非如此。
反而因为析构函数的多态非常重要,C++又专门为它开了一条路:
任意一个类,只要它加入了继承体系,那么它的析构函数的名字就会被改成destructor
因为析构函数没有返回值和参数表,所以析构函数构成重写非常简单,只需要在父类的析构函数前面加一个virtual,让它变成虚函数就可以了。
为什么有必要要让析构函数构成重写?
如果析构函数没有构成重写,那么下面这种情况就会内存泄露:
因为析构函数没有构成多态,所以delete时候只会看指针的类型是什么,据此调用析构函数
所以只调用了父类的析构
当析构函数构成多态的时候:
因为析构函数构成了多态,所以delete时候就会看指针的指向的对象是什么,据此调用析构函数
所以会调用子类的析构