虚函数
什么是虚函数
类的成员函数前加virtual这种函数就叫虚函数
特点
子类会覆盖父类的虚函数
多态
当子类覆盖了父类的虚函数时,通过父类指针指向子类对象时,调用虚函数,会根据具体的对象是谁来决定执行谁的函数,这就是多态。
多态的条件
1、父子类之间函数有覆盖关系。
2、父类的指针或引用指向子类对象
在构造、析构函数中调用虚函数
1、在父类的构造函数中调用虚函数,此时子类还没有创建完成,因此只能调用父类的虚函数,而不是覆盖版本的虚函数
2、在子类的析构函数中调用虚函数,此时子类已经释放完成,只能调用父类的虚函数,而不是覆盖版本的虚函数。
纯虚函数
在虚函数的声明后面添加=0,这种虚函数就叫纯虚函数,可以不实现,但如果实现必须在类外(只能在父类的构造函数、析构函数中调用)。
virtual 返回值 函数名(参数) = 0;
抽象类
1、成员函数中有纯虚函数,这种类叫抽象类,抽象类不能实例化(不能创建对象)。
2、抽象类必须被继承且纯虚函数被覆盖后,由子类实例化对象。
3、如果继承抽象类,但没有覆盖纯虚函数,那么子类也将称为抽象类,不能实例化。
纯抽象类
1、所有成员函数都是纯虚函数,这种只能被继承的类叫纯抽象类。
2、这种类一般用来设计接口,这种类在子类被替换后不需要被修改,或少量的修改即可继续使用。
虚函数表
什么是虚函数表
在C++的类中,一旦成员函数中有虚函数,这个类中就会多一个虚函数表指针,这个指针指向一个虚函数表,表里记录了这个类中所有的虚函数,当这个类被继承,它的子类也会有一个虚函数表(不管子类中有无虚函数),如果子类的成员函数中有函数签名与父类的虚函数一样的,就会用子类中的函数替换它在虚函数表中的位置,这样就达到了覆盖的效果。
当通过类指针或引用调用函数时,会根据对象中实际的虚函数表记录来调用函数,这样就达到了多态的效果。
虚析构
- 当使用delete释放一个父类指针时,不管实际指向的对象时子类还是父类都只会调用父类的析构函数。
- 如果子类的析构函数有需要释放的内存,就会造成内存泄漏。
- 为了解决这个问题,可以把父类的析构函数设置为虚函数,析构函数进行覆盖时不会比较函数名。
- 当父类的析构函数为虚函数时,通过父类指针或引用释放子类对象时,会自动调用子类的析构函数,子类的析构函数完成后也会调用父类的析构函数。
注意:析构函数可以是虚函数,但构造函数不行
强制类型转换
- 注意:C++中为了兼容C语言,(目标类型)源类型 依然可以继续使用,但C语言中的强制类型转换安全性差,因此建议使用C++中的强制类型转换。
- 注意:C++之父认为如果代码设计的很完善,根本不需要用到强制类型转换,而C++的强制类型转换之所以设计的很复杂,是为了让程序员多关注代码本身的设计,尽量少使用。
- C++中的强制类型转换保证没有很大的安全隐患
- static_cast<目标类型>(源类型) 编译器会对源类型和目标类型做兼容性检查,不通过则报错.
- dynamic_cast<目标类型>(源类型) 编译器会对源类型和目标类型是否同为指针或引用进行检查,并且存在多态型的继承关系(里面要有虚函数)
- const_cast<目标类型>(源类型) 编译器会对源类型和目标类型是否同为指针或引用进行检查,除了常属性之外其他必须完全相同否则报错.
- reinterpret_cast<目标类型>(源类型) 编译器会对源类型和目标类型是否同为指针和整数进行检查,也就是说把整数转换为指针或把指针转换为整数
动态编译和静态编译
静态编译:指针或引用的目标是确定的,在编译时就确定所有类型检查和函数调用.
动态编译:指针或引用的目标是不确定的(多态),只有函数调用时候才确定具体是哪个子类.