C++:多态

本文深入探讨面向对象编程的核心概念,包括封装、继承与多态,解析vftable与vfptr机制,阐述析构函数为何设为虚函数的原因,以及多继承与菱形继承的挑战与解决策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

面向对象思想:面向对象是将功能等通过对象来进行实现,让对象去实现具体的细节,这种思想是将数据作为第一位,而方法以及算法为次要的,这是对数据的一种优化,简化了过程。

面向对象的三大特性:封装性、继承性、多态性

封装性:是将一类事物或者行为抽象成一个类,使其属性私有化,行为公有化,提高了数据隐私性,这样使得代码的复用性更高。

继承性:是进一步将一类事物共有的属性和行为抽象成一个父类,而每一个子类是一个特殊的父类,它既有父类的属性和行为同时又有属于自己的属性和行为。这样做扩展了已经存在的代码块,进一步提高了代码的复用性。

多态性:继承和封装是为了代码复用,而多态是为了实现接口复用。“一个接口,多种方法”,多态允许父类引用指向子类对象。同一个操作作用于不同的对象,可以有不同的解释,有不同的结果。而多态的存在是由条件的:继承的存在、子类重写父类方法、父类引用变量指向子类对象。

 

vftable(虚函数表):vftable中存放的是虚函数的地址。其中vftable的前4个字节存放的是RTTI指针,指向的是RTTI信息,也就是一个运行时类型识别功能,接下来的4个字节是偏移量,表示虚函数指针在对象中的偏移量,后面是虚函数的地址,vftable是编译阶段产生的,运行时加载到内存中,最后4个字节是NULL,表示虚函数表的结束。

vfptr(虚函数指针):vfptr是运行阶段赋值,它是根据vftable加载到内存中的地址进行赋值的,先调用基类的构造函数把基类的虚函数表地址赋值到虚函数指针上,但又在自身构造函数或者初始化列表之前,再次让vfptr指针指向派生类类型的虚函数表,对继承过来的基类的虚函数表进行函数覆盖,这是多态的关键!

首先,构造函数和静态成员函数不能是虚函数!这是因为构造函数之前还没有构造成对象,因此没有对象的地址。而静态成员函数通过类的作用域就可以调用函数,根本不需要生成对象,调用静态函数是静态绑定的过程,这与虚函数的动多态相反。

其次,友元函数也不能是虚函数!因为在一个类里声明友元时,由于友元不是自己的成员函数自然不能再自己的类里把它声明成虚函数,友元函数能通过类的对象访问类的所有成员,友元函数是不能直接访问类中的私有成员的。

最后,析构函数为什么是虚函数?现在假如基类的析构函数不是虚函数,delete基类指针时,基类的析构调用是静态的,只能调用基类的析构函数,那么现在派生类就无法释放了,这样就造成了内存泄漏!假如现在基类的析构是虚函数,基类就有一张vftable,派生类继承基类时会把自己的析构函数覆盖到vftable,delete基类指针发生了动态绑定,调用的是该派生类析构而派生类析构会先释放派生类对象,再释放基类对象。

多继承

菱形继承

缺陷:

(1)增加程序的复杂度,容易出错

(2)继承类和基类中的同名函数产生二义性

(3)多级继承,会产生数据冗长

(4)构造派生类对象时基类的初始化顺序以及成员会不小心在派生类中隐藏的问题

 

多继承的内存布局(虚函数)

菱形继承以及解决方案

 

消除菱形继承二义性(使用虚拟继承 虚基类)

重载与重写

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值