大家默认说的多态是运行时多态
编译期多态,正如其名,就是在编译期确定的一种多态性。这个在C++中主要体现在函数模板,这里需要注意的是函数重载和多态无关,很多地方把函数重载也误认为是编译期多态,这是错误的。
那么函数模板是如何体现编译期多态的呢?下面举一个简单的例子就可以明白。
// 例1:函数模板体现出编译期多态
#include <iostream>
template <typename T>
T add(T a, T b)
{
T c = a + b;
return c;
}
int main()
{
int i1 = 1;
int i2 = 2;
int iResult = 0;
iResult = add(i1, i2);
std::cout << "The result of integer is " << iResult << std::endl;
double d1 = 1.1;
double d2 = 2.2;
double dResult = 0;
dResult = add(d1, d2);
std::cout << "The result of double is " << dResult << std::endl;
return 0;
}
从例1中可以看到,我们定义了一个函数模板add,用来求两个数的和,这两个数的数据类型在使用时才知道。main函数中使用了两个int值的求和以及两个double值的求和,这里就体现了多态性,即在编译期,编译器根据一定的最佳匹配算法确定函数模板的参数类型到底是什么,这就体现了编译期的多种状态。
当说到多态性的时候一般都默认指运行期多态,所以编译期多态大家只要知道是如何表现的就可以了,下面重点来讨论运行期多态
运行期多态主要是指在程序运行的时候,动态绑定所调用的函数,动态地找到了调用函数的入口地址,从而确定到底调用哪个函数。在C++中,运行期多态主要通过虚函数来实现,并且一定要有继承关系,下面举一个简单的例子来讲解
// 例2:虚函数和继承关系体现运行期多态
#include <iostream>
class parent
{
public:
parent() {}
// 父类的虚函数
virtual void eat()
{
std::cout << "Parent eat." << std::endl;
}
// 注意这个并不是虚函数!!!
void drink()
{
std::cout << "Parent drink." << std::endl;
}
};
class child : public parent
{
public:
child () {}
// 子类重写了父类的虚函数
void eat()
{
std::cout << "Child eat." << std::endl;
}
// 子类覆盖了父类的函数,注意由于父类的这个函数
// 并不是虚函数,所以不存在继承后重写的说法
void drink()
{
std::cout << "Child drink." << std::endl;
}
// 子类特有的函数
void childLove()
{
std::cout << "Child love playing." << std::endl;
}
};
int main()
{
parent* pa = new child();
pa->eat(); // 运行期多态的体现!!!
pa->drink(); // 非多态就是隐藏,取决于等号左边这里调用的还是父类的drink,所以并不是多态!!!
// pa->childLove(); // 编译出错,父类的指针不能调用父类没有的函数
return 0;
}