学习C++的多态,必然听说过虚函数和纯虚函数。
要学习虚函数和纯虚函数,首先我们要搞清楚它的语法规范。总结一下无非下面几点:
- 在类成员方法的声明(注意不是定义)前面加个
virtual
,该函数就变为虚函数,在虚函数声明语句后面加个=0
,虚函数就变为纯虚函数。 - 当类中有了纯虚函数,这个类也称为抽象类
- 子类可以重新定义基类的虚函数,我们把这个行为称之为复写(override)。
- 子类可自主选择是否要提供一份属于自己的个性化虚函数实现,但是必须提供一份属于自己的个性化纯虚函数实现。
虚函数和纯虚函数出现的意义何在呢,我们用一个例子来说明它们是如何实现多态的:
假设我们设计一个饮品做法的类,并提供类似冲泡材料,冲泡时间的实现代码,而饮品的样式也是多种多样,有茶水,咖啡,奶茶等等。然后我们假设有一个饮品制造者,他可以做很多样式的饮品。
首先我们假设所有的饮品做法都有冲泡的水,冲泡材料,冲泡时间。我们将这三种行为抽象到一个抽象类中,并由它来派生具体的饮品。
class AbstractDrinking
{
public:
//冲泡的水
virtual void Water();
//冲泡时间
virtual void Time() = 0;
//冲泡材料
virtual void Ingredients() = 0;
void MakeDrink() {
Water();
Time();
Ingredients();
}
};
由此我们有了AbstractDrinking
这个基类,当中有冲泡的水,冲泡材料,冲泡时间三个行为和一个执行流程函数,其中冲泡的水是虚函数,而另外两个是纯虚函数。
Water()是一个普通虚函数,基类希望子类提供自己的个性化实现代码,但基类同时也提供一个缺省的虚函数实现版本,在子类不复写该虚函数的情况下作为备选方案
void AbstractDrinking::Water() {
cout << "200ml纯净水。" << endl;
}
然后根据这个AbstractDrinking
基类,我们可以派生出其他各种各样的饮料派生类。
茶类定义
class Tea : public AbstractDrinking
{
//这里我们复写了纯虚函数Time,Ingredients和虚函数Water
virtual void Water() {
cout << "250ml农夫山泉。" << endl;
}
virtual void Time() {
cout << "10min。" << endl;
}
virtual void Ingredients() {
cout << "龙井" << endl;
cout << "-----------------------" << endl;
}
};
咖啡类定义
class Coffee : public AbstractDrinking
{
//这里我们复写了纯虚函数Time和Ingredients,而没有复写虚函数Water
virtual void Time() {
cout << "5min。" << endl;
}
virtual void Ingredients() {
cout << "咖啡、牛奶、方糖。" << endl;
cout << "-----------------------" << endl;
}
};
以上代码可以看到,咖啡类没有复写Water而是使用基类提供的Water。
然后我们来定义一个优秀的饮品制造者。
class DrinkingMaker
{
public:
void DoWork(AbstractDrinking * drink) {
drink->MakeDrink();
delete drink;
}
};
然后再在主函数当中实现:
int main()
{
DrinkingMaker Jack;
Jack.DoWork(new Coffee);
Jack.DoWork(new Tea);
system("pause");
return 0;
}
总结一下:
当基类的某个成员方法,在大多数情形下由子类提供个性化实现,但基类也可以提供一个备选方案的时候,将其设计为虚函数。
当基类的某个成员方法,必须由子类提供个性化实现的时候,将其设计为纯虚函数。
纯虚函数在基类中的实现跟多态性无关,它只是提供了一种语法上的便利,在变化多端的应用场景中留有后路。