虚函数

虚函数定义
虚函数是在基类中声明为virtual,并在派生类中被重新定义的成员函数。
引入虚函数的原因
基类指针调用派生类的成员函数。
虚函数表
含有虚函数的类中,编译器会自动为每个对象创建一个隐藏的虚指针,虚指针指向一个全局的虚表。虚表存放若干函数指针,指向类中的虚函数。
虚表是属于类的,而不是某个对象的。虚表存放在模块的数据段中。数据段存放模块的全局数据和静态数据。
当子类重写父类虚函数时,会把继承自父类的虚函数表对应函数的索引函数指针从父类函数改成自己的。
虚指针
虚指针4字节,存放在对象最前面位置,保证正确取到虚函数的偏移量。
虚函数原理
虚函数是动态联编的,即在运行时根据对象类型确定调用到。
虚函数和常规函数的区别
常规函数是静态联编的,即在编译时根据指向对象的指针类型确定调用的。
常规函数的好处
常规函数的好处是效率比虚函数高。同样被调用过程中,虚函数表机制占用额外内存。
虚函数不能是默认成员函数的原因
虚函数的优点主要体现基类实现多态上,并不是所有类都是基类,虚函数表机制占用额外内存。
其次,所有函数定义为虚函数会产生语义上的歧义,隐藏了程序员的设计性。
析构函数设置为虚函数
被继承基类的析构函数设为虚函数。基类指针指向派生类对象时,释放基类指针可以释放派生类空间,防止内存泄漏。(先调用派生类的析构函数,再调用基类的析构函数。)
虚函数的限制
静态成员函数、内联函数、构造函数不能是虚函数。
构造函数不能是虚函数的原因
虚函数是动态联编的,即在运行时根据对象类型确定调用的。因此构造对象期间,编译器无法确定对象实际类型。
虚函数执行依赖虚表,而虚表指针在构造函数内初始化。因此构造对象期间,虚表还未被初始化。
构造函数不能调用虚函数的原因
派生类构造过程中,首先调用基类构造函数,而此时派生类还未创建完成。调用的虚函数仍然是基类的虚函数。
多态
多态分为静态和动态。静态多态是重载(函数重载、操作符重载)。动态多态是虚函数。
多态就是基类的不同派生类相同功能的不同实现方法。
(封装、继承、多态是面向对象语言的三大特性。)
纯虚函数
纯虚函数在基类中没有定义,但要求派生类实现方法。
含有纯虚函数的类为抽象类。抽象类不能生成对象,只能通过指针或引用。
派生类实现纯虚函数,纯虚函数就变成虚函数,没有实现的派生类仍然是抽象类。
纯虚函数的格式
virtual void func() = 0;
引入纯虚函数的原因
很多情况,基类本身对象是不合理的,不可实例化。引入纯虚函数更加安全,编码更加高效。
接口
C++接口由虚函数实现。
接口是特殊的抽象类。类中没有成员变量且所有成员函数都是公有的。(所有成员函数都是纯虚函数。)
抽象类可以替代接口吗?
接口是特殊的抽象类。不考虑多继承的情况下,可以替代。
(单继承指子类只有一个父类,多继承指子类可以有多个父类。C#中不支持多继承,但基类可以有多个派生类。继承可以传递。)
接口和抽象类的区别
接口是动作的抽象,抽象类是根源的抽象。
接口支持多继承,抽象类不支持。
接口要求派生类必须实现方法,抽象类不要求。
接口在扩展性和多层继承上比较麻烦,抽象类比较灵活。
(扩展性是指派生类必须实现接口的所有方法。接口只能单层继承。)
接口和抽象类使用场景
接口表示动作抽象,用于多继承、派生类必须实现方法。
抽象类表示根源抽象,约束派生类但不要求必须实现方法,能包含实例变量,可用于多层继承。
接口和抽象类同名方法时
不同文件下可以。同一文件下,在编译后名字相同会出现覆盖情况。

全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务