理解多态到底是什么
静态(编译时期)的多态:函数重载、模板(函数模板和类模板):
bool compare(int, int){}
bool compare(double, double){}动态(运行时期)的多态:
在继承结构中,基类指针(引用)指向派生类对象,通过该指针(引用)调用同名覆盖方法(虚函数),基类指针指向哪个派生类对象,就会调用哪个派生类对象的同名覆盖方法,称为多态。
class Cat :public Animal
{
public:
Cat(string name) :Animal(name) {}
void bark() { cout << _name << "bark:miao miao!" << endl; }
};
class Dog :public Animal
{
public:
Dog(string name) :Animal(name) {}
void bark() { cout << _name << "bark:wang wang!" << endl; }
};
class Pig :public Animal
{
public:
Pig(string name) :Animal(name) {}
void bark() { cout << _name << "bark:heng heng!" << endl; }
};
/*
以下这组bark API接口无法做到我们软件设计要求的"开-闭"原则
"开-闭"原则 --- 对修改关闭,对扩展开放
*/
// 即便是增加动物实体类,也不用增加bark接口,这是一个通用的接口函数
void bark(Animal *p)
{// 动态的多态
p->bark(); // 发生动态绑定,调用各个派生类重写后的bark方法
}
int main()
{
Cat cat("猫");
Dog dog("狗");
Pig pig("猪");
bark(&cat);
bark(&dog);
bark(&pig);
return 0;
}
提供一个通用的接口函数,以基类指针接收不同派生类对象的地址,并且通过基类指针调用不同派生类对象的同名覆盖方法(虚函数)
理解抽象类
看下面例子:
定义Animal的初衷,并不是让Animal抽象某个实体的类型,而是为了:
- string _name; 派生类通过继承抽象类,实现成员变量的复用
- 给所有的派生类保留统一的覆盖/重写接口
实现一个抽象类:
class Animal
{
public:
Animal(string name) :_name(name) {}
// 纯虚函数
virtual void bark() = 0;
protected:
string _name;
};
拥有纯虚函数的类,叫做抽象类
抽象类不能再实例化对象,但是可以定义指针或者是引用变量(这样就可以进行动态绑定了—>多态)
再举个例子
// 把基类Car设置成抽象类,不用实例化对象
// 只负责提供复用的代码以及统一接口
class Car
{
public:
Car(string name, double oil) :_name(name),_oil(oil) {}
// 获取汽车剩余油量还能跑的公里数
double getLeftMiles() // 普通函数中调用虚函数
{
return _oil * getMilesPerGallon(); // 发生动态绑定
}
string getName()const { return _name; }
protected:
string _name;
double _oil;
virtual double getMilesPerGallon() = 0; // 纯虚函数
};
- 定义派生类
class Bnze :public Car
{
public:
Bnze(string name, double oil) :Car(name, oil) {}
double getMilesPerGallon() { return 20.0;}
};
class Andi :public Car
{
public:
Andi(string name, double oil) :Car(name, oil) {}
double getMilesPerGallon() { return 18.0; }
};
class BMW :public Car
{
public:
BMW(string name, double oil) :Car(name, oil) {}
double getMilesPerGallon() { return 19.0; }
};
void showCarLeftMiles(Car &car)
{
// 静态绑定 call Car::getLeftMiles(),但是getLeftMiles()中getMilesPerGallon()的调用是动态绑定的
cout << car.getName() << " LeftMiles:"<< car.getLeftMiles()<<endl;
}
int main()
{
Bnze b1("奔驰", 20.0);
Andi a("奥迪", 20.0);
BMW b2("宝马", 20.0);
showCarLeftMiles(b1);
showCarLeftMiles(a);
showCarLeftMiles(b2);
return 0;
}