多态概述
所谓“多态”,字面意思即“多种形态”。常常会在类的继承中出现,通常还与虚函数、纯虚函数相关。
为了更好地了解多态,请看下面的实例:
#include <cstdio>
class father
{
public:
void use()
{
printf("using class father\n");
return;
}
};
class sona : public father
{
public:
void use()
{
printf("using son a\n");
return;
}
};
class sonb : public father
{
public:
void use()
{
printf("using son b\n");
return;
}
};
int main()
{
sona A;
sonb B;
father* s;
s = &A;
s->use();
s = &B;
s->use();
return 0;
}
运行上面的代码,将会输出以下结果:
using class father
using class father
这显然与我们的预期不匹配。编译器调用了father
类中的函数use()
,而不是sona
和sonb
类中的函数use()
。为什么呢?
其实编译器在编译过程中已经将use()
函数与基类father
中的use()
函数相绑定,或者说相链接:这种在编译过程中就将函数绑定于基类的动作叫做静态多态,或者叫做静态链接,有时也叫做早绑定。这样的行为让系统在调用函数时自动调用基类中的函数,因此会出现与预期不匹配的情况。
解决问题的方法:虚函数
如果想要解决上述代码的问题,就需要使用虚函数。声明一个函数是虚函数,需要使用关键字virtual
。更改后的代码如下:
#include <cstdio>
class father
{
public:
virtual void use()
{
printf("using class father\n");
return;
}
};
class sona : public father
{
public:
void use()
{
printf("using son a\n");
return;
}
};
class sonb : public father
{
public:
void use()
{
printf("using son b\n");
return;
}
};
int main()
{
sona A;
sonb B;
father* s;
s = &A;
s->use();
s = &B;
s->use();
return 0;
}
我们仅仅加了一个virtual
关键字,程序的输出就会变为:
using son a
using son b
这是符合我们预期的。
那么这个virtual
关键字到底有什么用呢?其实virtual
关键字会告诉编译器,不执行早绑定的动作,而是将函数绑定到派生类的函数上去。这种操作叫做动态链接,或者后期绑定。
这就是多态的一般实现。有了多态,基类和派生类就可以拥有同名函数,它们的参数列表甚至可以相同。
更进一步:纯虚函数
纯虚函数的声明框架如下:
virtual func_type func_name() = 0;
“=0”告诉编译器函数是纯虚函数。纯虚函数用于在基类中无法给予具体功能,需要在派生类中进一步具体定义函数功能。
总结/小注
virtual
关键字只能用于类内声明,类外的virtual
关键字无效。