|
5. 虚拟功能和覆盖
1.虚函数
当成员函数以虚装饰为前缀时,这样的函数称为虚函数
该类还将有一个虚拟指针,如虚拟继承
2.虚函数表
虚拟指针指向一个表的首地址,该表记录了类中所有虚函数的首地址
((void(*)(void))(*(int*)*(int*)b))();//相当于调用虚函数表中的第一个函数
3.封面
当使用virtual关键字修改父类的成员函数时,父类中有一个虚拟指针(虚表指针),子类会一起继承父类的虚拟指针。
当子类中存在与父类的虚函数同名的成员函数时,编译器会比较这两个函数的格式。
覆盖虚函数表中父类同名虚函数的原地址
此时,当使用父类的指针或引用指向子类对象时,调用虚函数会调用被覆盖的虚函数表中指向的同名同格式的子类。
成员函数
4. 构成保障的条件
1.必须发生在父类和子类中,并且必须是公有继承
2.要覆盖的父类的函数必须修改为虚函数
3、子类必须有一个与父类虚函数同名的函数,并且返回值、参数列表、常量属性必须完全一致才能构成覆盖
4.重写要求返回值类型相同,或者子类函数的返回值可以隐式转换为父类虚函数的返回值类型并且有继承关系,也可以构成
覆盖
面试常见问题:重载、覆盖、隐藏、覆盖有什么区别?
隐藏:
1.如果名称相同但格式不同,无论是否添加virtual,同名父类的成员函数都会隐藏在子类中
2.如果子类同名同格式,不加virtual,子类也会隐藏父类同名的成员函数
总结:父类和子类中隐藏泛目录,同名的成员函数要么构成覆盖,要么隐藏
7. 多态性
什么是多态性:
这意味着相同的事物和指令可以有多种形式。当调用同一条指令时,它会根据不同的参数和环境做出不同的对应操作。这种模式
多态性
在C++中,多态根据确定操作的时间分为编译时多态和运行时多态。
编译时多态性:
调用重载函数时,编译器可以根据不同的参数,在编译时确定执行哪个版本的重载函数。这称为编译时多态性。
和模板技术
运行时多态性:
在父子类中,当子类重写同名父类的虚函数,然后使用父类指针或引用访问虚函数时,可能调用父类的虚函数隐藏泛目录,也可能调用父类的虚函数。
调用子类的同名函数,调用哪一个取决于父类指针或引用的目标指向谁,而这个目标确实需要在运行时敲定
确定了,这种情况称为运行时多态
构成运行时多态性的条件:
1.父类和子类之间存在同名函数
2.子类通过public方式继承父类(让父类指针和引用指向子类对象)
3.通过父类指针或引用访问被覆盖的虚函数
思考:构造函数和析构函数可以是虚函数吗?为什么?
八、虚析构和虚构造
虚拟建筑:
构造函数不能设置为虚函数。如果构造函数可以设置为虚函数,子类的构造函数会自动覆盖父类的构造函数。创建子类对象时,
在子类执行自己的构造函数之前,先执行父类的构造函数,但是此时父类的构造函数已经被子类的构造函数覆盖,会再次被调用。
子类的构造函数,形成无限循环,所以编译器不允许将构造函数声明为虚函数
虚拟析构函数:
析构函数可以设置为虚函数。使用类多态时,通过父类指针或引用释放子类对象时,默认情况下,没有虚析构函数不会调用子类。
析构函数,如果子类析构函数中有资源需要释放,则可能构成内存泄漏
只有父类的析构函数被定义为虚析构函数(子类的析构函数会自动覆盖父类的析构函数),当子类对象通过父类指针或引用释放时,
被覆盖的子类的析构函数会先被调用,然后会自动调用父类的析构函数,这样就不会发生内存泄漏。
总结:
当使用多态且子类构造函数已分配内存时,必须将父类的析构函数设置为虚拟析构函数
9.纯虚函数和纯抽象类
纯虚函数的格式:
虚拟返回值成员函数名(参数列表)=0;
1.纯虚函数只能声明不能实现(一般不需要实现)
2.如果父类中有纯虚函数,子类必须重写父类的纯虚函数,否则无法创建对象
3.带有纯虚函数的类不能创建对象,因为这种情况下纯虚函数没有被覆盖
4.纯虚函数的目的是强制子类覆盖父类的纯虚函数,强制子类实现某些功能
5.具有纯虚函数的类称为抽象类
6.析构函数可以定义为纯虚函数,但需要在类外实现
纯抽象类:
所有成员函数都是纯虚函数类,称为纯抽象类,一般用于设置功能接口,也称为接口类
豪侠泛目录站群程序,专业泛目录,站群,二级目录,泛站群程序! |
|