基类指针可以按照基类的方式做事,也可以按照派生类的方式做事,即有不同的多种的表达形式,这种现象称之为多态。
C++多态的目的是可以通过基类指针对所有派生类的成员和函数进行全方位的访问。无多态的话,只能访问成员变量。
1.1,C++多态的实现
C++的多态必须满足两个条件:
1 必须存在继承关系
2 继承关系中必须有同名的函数,并且是覆盖关系(函数原型相同)。
3存在基类的指针 ,通过该指针调用虚函数。
然而有了虚函数,基类成员指向基类成员对象时就使用基类的成员,指向派生类成员可以使用派生类的成员。
1.1.1,引用实现多态
#include<iostream>
using namespace std;
class Drink{
public:
virtual void Show();
}
void Drink::Show(){
cout<<"Drink"<<endl;
}
class Water:public Drink{
public:
void Show();
}
void Water::Show(){
cout<<"Water"<<endl;
}
Class Milk:public Drink{
public:
void Show();
}
void Milk:Show(){
cout<<"Milk"<<endl;
}
class Coffee:public Drink{
public:
void Show();
}
void Coffee::Show(){
cout<<"Coffee"<<endl;
}
void Bottle(Drink &rd){
rd.Show();
}
int main(){
Water a1;
Milk a2;
Coffee a3;
Drink &ra1=a1;
Drink &ra2=a2;
Drink &ra3=a3;
Bottle(ra1);
Bottle(ra2);
Bottle(ra3);
return 0;
}
1.1.2虚函数
virtual void fun() //error! 在类外面的函数不能是虚函数
{}
1.virtual修饰的关键字就是虚函数。
虚函数只能是类中非静态的成员函数
2.为了方便,你可以只将基类中的函数声明为虚函数,这样所有派生类中具有遮蔽关系的同名函数都将自动成为虚函数。
3.当在基类中定义了虚函数时,如果派生类没有定义新的函数来遮蔽此函数,那么将使用基类的虚函数。构造函数不能是虚函数。对于基类的构造函数,它仅仅是在派生类构造函数中被调用,这种机制不同于继承。也就是说,派生类不继承基类的构造函数,将构造函数声明为虚函数没有什么意义。析构函数可以声明为虚函数,而且有时候必须要声明为虚函数。
1.1.3虚函数表
每一个有虚函数的类都有一个虚函数表,这类的任何对象中都放有该虚函数表的指针。它位于对象存储空间的最前端,其中存放的是虚函数表的地址。
如
class A
{
public:
virtual void fun()
{}
protected:
int _a;
};
除了成员变量之外,还多了一个指针,在调用虚函数的时候,编译器就会通过虚标指针去虚表里面查找。
子类的虚函数表一部分继承自父类。如果重写了虚函数,那么子类的虚函数会在虚表上覆盖父类的虚函数。
如果继承了虚函数,那么
1 子类先拷贝一份父类虚表,然后用一个虚表指针指向这个虚表。
2 如果有虚函数重写,那么在子类的虚表上用子类的虚函数覆盖。
3 子类新增的虚函数按其在子类中的声明次序增加到子类虚表的最后。
1.1.4虚析构
为了delete基类时指针指向派生类时防止子类的数据不被释放而造成内存的泄露
例
class ObjectBase
{
public:
ObjectBase() {
}
virtual ~ObjectBase() {
std::cout << "Delete ObjectBase" << std::endl;
}
};
1.1.5纯虚函数
virtual void test() = 0;
纯虚函数出现的原因:
(1)多态,在基类中定义纯虚函数,在派生类中对此函数进行重写;
(2)有些时候在基类中生成哪些对象并不是很合理,就会定义纯虚函数在派生类中生成;
声明了纯虚函数的类是一个抽象类,它不能创建类的实例,只能派生类通过重写创建实例,基类并不会关心派生类将实例重写成什么样子。