函数重写overwrite:当子类提供了和父类同名的虚函数时,称之为函数重写,函数的返回值类 函数名 参数列表必须完全相同
名字隐藏namehide:当子类提供了和父类同名的数据时 叫名字隐藏
函数重载:同一个作用域中 函数名相同 参数列表不同的函数构成重载
多态
当父类型的指针(引用)指向子类对象时,通过父类型的指针 调用虚函数,如果子类重写了这个虚函数,则调用的表现是子类的,否则就是父类型中对应的实现,多态使类型更加通用,根据具体的对象做出具体的行为
- 继承是构成多态的基础
- 虚函数是构成多态的关键
- 函数重写是必备条件
应用
用在函数的参数上
void testAnimal(Dog* dog); //不好
void testAnimal(Animal* dog); //好
用在函数的返回值上
Cat* getAnimal(int s); //不好
Animal* getAnimal(int s); //好
静态绑定:编译时确定调用的函数地址
动态绑定:运行时确定调用函数的地址
虚表虚指针
多态的底层实现,靠的是虚函数表(虚表),
虚指针,指向虚表的指针任何一个具有虚函数的类型 只有一张虚表 同类型的对象共享虚函数表,当一个父类型的指针指向一个子类型的对象时,调用虚函数时并没有立即生成函数的调用地址,而是先根据对象定位出对象的前4byte对象的虚函数表,再根据虚函数表中存放的函数地址进行调用,虚函数表中存放的是哪个函数,对应哪个类的实现,就调用相应的函数
类型识别
Q为什么要有类型识别
A:多态做到的效果是类型通用,但这样损失了对象的个性,有时候要恢复个性,根据具体的类型做出相应的个性展现
dynamic_cast<类型>(对象) //会尝试着把对象变成相应的类型,如果成功就返回非空,不成功就返回NULL
typeid
这个运算符可以获得类型或者对象的类型信息, typeid返回的信息存入一个type_info类型的对象中,这个类型重载==和!=运算符,并且有个成员函数name()返回类型的名称
/usr/include/c++/4.6 下有一个头文件#include
抽象类
不能实例化的类,除此之外和正常没有区别,只要在类中出现一个纯虚函数(pure),这个类就是抽象类
class A{
public:
virtual void show()=0; //纯虚函数
};
Note:如果一个类继承了抽象类,但没有实现其中的纯虚函数,那么这个子类也会变成抽象类
虚析构函数
加了virtual修饰的析构函数,当父类对象的指针指向子类对象时,如果释放指针对应的内存,只会调用父类对应的析构函数,子类析构行为未定义(不去调用)
如果把父类对应的析构函数改成虚析构,则会调用子类析构函数,同时由于子类的析构函数的调用必然会触发父类析构函数的调用,上述问题得以解决
Q:什么时候需要虚析构
A:当父类中有虚函数(此时常常需要用父类型对象指向子类型对象以实现多态)或者父子类型中都有堆内存处理,需要使用虚析构来释放资源