多态的分类
编译时多态:静态多态(函数重载,运算符重载)
运行时多态:动态多态(虚函数)
绑定:指把一个函数名和一个存储地址联系在一起的过程
编译时多态是指绑定工作在编译阶段完成,运行时多态是指绑定工作在运行阶段完成。
例如函数重载就是一种静态绑定,在编译阶段编译器就会根据每个函数的参数表来决定绑定哪个函数体。
运算符重载
两个对象,如两个复数类对象,用"+"、"-"符号,编译器是不认识的。运算符重载就是为了解决这个问题,为已有的运算符赋予多重含义,使同一运算符作用于不同的数据时导致不同的行为。
两种重载方式:
- 重载为类的非静态成员函数;
- 重载为非成员函数;(一般为把它写成友元函数,因为运算符重载本来就会频繁的访问类中的私有成员)
声明形式:
函数类型 operator 运算符(形参)
{
.......
}
重载为类成员函数时,参数个数可以少一个,因为调用这个成员函数的对象本来就是一个参数。
oprd1 + oprd2 ;//注意第一个参数必须为类的对象
oprd1.operator+ (oprd2);//编译器将上面的翻译成这句
//上面那句表达式的结果就是下面这个函数的返回值!!
但是重载为非成员函数时,一个也不能少。
有些场合只能用这种重载为非成员函数的形式,比如实数+复数,第一个参数为实数,并不能调用复数类中的成员函数。
虚函数
基类中如果有virtual函数,则告诉编译器不能将这个函数早绑定,而是晚绑定。
编译器做了一件伟大的事,为每一个有虚函数的类准备了一个虚函数表,这个表中存有该类的所有虚函数。编译器会给这个类的对象默认增加一个数据成员:指向虚函数表的指针。
void fun(Base1 *ptr){
ptr->display();
}
int main(){
Derived derived;
fun(&derived);//把派生类对象地址传给基类
}
如上面的例子,编译器给derived对象增加了一个指针derived1,指向它的虚函数表,表中存有虚函数display()的函数体地址。当我们将derived的地址传给ptr时,ptr指向derived1,derived1再指向虚函数表,这样就能实现动态绑定。
虚析构函数
当我们有重要的事情必须在对象消亡的时候做的话,我们就需要自己写析构函数。
那么如果出现下面的情况怎么办呢?
Base1* b1 = new Derived();
delete b1;
在delete b1的时候执行谁的析构函数呢?如果按照指针类型执行了基类的析构函数,那么我们写的派生类中的析构函数中要做的重要事情:比如释放某些内存空间、关闭文件等操作。结果都没做。我们把基类的析构函数写成虚析构函数,那么它就也会动态绑定。
纯虚函数
纯虚函数没有函数体,含有纯虚函数的类叫做抽象类。这样的类有些东西还没有实现,所以不能定义对象。那么用来干什么?
用来作为基类来规范整个类家族的统一对外接口。
继承了这个抽象类的派生类必须实现这个纯虚函数,否则将继续作为抽象类。