前言
多态多为C++的三大特性之一,我们对此的了解和应用大多停留在类继承层次和运行时阶段。那既然有运行(动态)时阶段,那是否也有静态的多态?有,那就是编译时多态
正文
动态多态
运行时的多态存在于继承类中,通过虚函数实现动态选择调用。简单的说就是通过一张虚函数表——vptable,vptable中记录了基类的虚函数地址,在派生类中继承了基类的这张vptable,而且在派生类的构造函数中对继承来的vptable相关地址进行替换,因此在调用时通过vptable的实际地址能够知道调用哪个函数。下面是个简单的范例:
#pragma once
#include <iostream>
class CBase
{
public:
CBase(){std::cout<<"CBase::CBase"<<std::endl;}
~CBase(){std::cout<<"CBase::~CBase"<<std::endl;}
virtual int getMax(int a, int b)
{
std::cout<<"CBase::getMax"<<std::endl;
return a>b?a:b;
}
};
class CChild : public CBase
{
public:
CChild(){std::cout<<"CChild::CChild"<<std::endl;}
~CChild(){std::cout<<"CCHild::~Child"<<std::endl;}
virtual int getMax(int a, int b)
{
std::cout<<"CChild::getMax"<<std::endl;
return a>b?a:b;
}
};
void main()
{
CBase *pBase = new CBase;
CChild *pChld = new CChild;
pBase->getMax(5, 2);
pBase = new CChild;
pBase->getMax(5,2);
getchar();
return;
}
输出如下:
总之动态多态发生在继承关系的类中,通过虚函数表实现。那静态多态呢?
静态多态
静态多态是发生在编译时期的,通过模板和函数重载实现,相比动态多态不需派生关系。下面看一个范例
class Animal
{
public :
void shout() { cout << "发出动物的叫声" << endl; };
};
class Dog
{
public:
void shout(){ cout << "汪汪!"<<endl; }
};
class Cat
{
public:
void shout(){ cout << "喵喵~"<<endl; }
};
class Bird
{
public:
void shout(){ cout << "叽喳!"<<endl; }
};
template <typename T>
void animalShout(T & t)
{
t.shout();
}
int main()
{
Animal anim;
Dog dog;
Cat cat;
Bird bird;
animalShout(anim);
animalShout(dog);
animalShout(cat);
animalShout(bird);
getchar();
}
上例就是通过模板实现静态多态,重载范例就不展示了。那么这两者区别在于什么?
编译时多态和运行时多态区别:
1、运行时体现了面向对象的思想,但是虚函数表增加了内存开销
2、运行时多态发生在运行时期,所以编译器无法进行优化处理
3、编译时多态运用泛型编程,在编译器完成提升运行期效率,但是无法实现模板的分离编译所以对于大工程而言编译耗时
4、编译时多态无法处理异质对象集合(什么是异质对象?)
延申拓展:显示接口和隐士接口
显示接口就是指能够明确来源的接口,比如在动态多态的范例中,当调用getMax时,我们能够知道这个getMax到底是CBase的还是CChild的
隐式接口就是指那些无法确定的来源的接口,如对于函数重载和模板,我们也不知道调用的是哪个实现。
解答:
异质类:就是(存储、指向)类型不一致的数据对象,如pBase,或Template<type T>,所以异质类的实现核心就是多态+模板编程