C++多态

多态

多态就是多种形态,同一行为产生多种的状态。

多态是在不同继承关心的类对象,去调用同一函数,产生了不同行为,多态在继承的继承上的。
多态的两个条件:
1.基类中必须包含虚函数,在派生类中必须对基类的虚函数完成重写。
2.虚函数的调用:必须通过基类的指针或者引用调用虚函数。
表现方式:在程序运行时,基类的指针或引用指向不同类的对象时,调用不同类的虚函数。

只要成员函数和基才能被virtual所修饰!!

虚函数

虚函数:就是在类成员函数前面加virtual关键字
虚函数重写:派生类中有一个跟基类完成相同的虚函数,我们就称子类的虚函数重写了基类的虚函数。完全相同是指:**函数名,参数,返回值都相同。**另外虚函数的重写也叫做虚函数的覆盖。

覆盖的两个例外:1.协变(返回值类型不同) 2.析构函数(函数名不同)

协变
重写的虚函数的返回值可以不同,但是必须分别是基类指针和派生类指针或者基类引用和派生类引用。

析构函数重写
防止内存泄漏,基类中的析构函数如果是虚函数,那么派生类的析构函数就重写了基类的虚构函数。这里他们的函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数名称做了特殊的处理,编译后的析构函数的名称统一处理为destructor,也就是说明基类的析构函数最好写成了虚函数。
子类虚函数可以与基类虚函数的访问权限不同。

override,修饰派生类虚函数,实现强制虚函数的重新,检查派生类是否重写了基类的虚函数。
fianl:修饰虚函数,不能被派生类重写

不规范的重写
在派生类中重写的成员函数可以不加virtual关键字,也是构成了重写,因为继承后基类的虚函数被继承下来在派生类依旧保持了虚函数属性,我们只是重写了它。但这是非常不规范的,我们平时不用这样用。

接口继承与实现继承
普通函数的继承是一种实现继承,派生类继承了基类函数,可以使用函数,继承的是函数的实现。虚函数的继承是一种接口继承,派生类继承的是基类虚函数的接口,目的是为了重写,达成多态,继承的是接口。如果不是实现是多态,不用把函数定义成虚函数。

重载:1.两个函数在同一作用域 2.函数名/参数相同

重写(覆盖) 1.两个函数分别是在基类和派生类的作用域
2.函数名/参数/返回值都必须完全相同(协变除外)
3.两个函数必须是虚函数

重定义(隐藏) 1.两个函数分别在基类和派生类的作用域
2.函数名相同
3.两个基类和派生类的同名函数不构成重写就是重定义

抽象类
在虚函数的后面写上=0,则这个函数为纯虚函数。包含纯虚函的类叫抽象类(接口类),抽象类不能实例化出对象。派生类继承后不能实例出对象,只要重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。

但抽象类的指针可以创建

虚函数表与虚函数指针

如果一个类中包含有虚函数,在vs2013平台下该类的对象模型就会多4个字节。多4个字节就是虚表指针,指向虚函数表(虚表)
基类虚表构造过程:
按照虚函数在类中的声明次序将虚函数入口地址存储到虚函数表中。

基类和子类是不同的虚表。
派生类虚表构建过程:

  1. 将基类虚函数表中的内容拷贝一份放置到子类虚函数中。
  2. 如果派生类重写了基类中某个虚函数,用派生类虚函数替换(覆盖)虚表中相同偏移量位置的基类虚函数
  3. 如果派生类增加了新的虚函数,按照新增加虚函数在虚表中声明次序将其添加到虚表的最后。

虚函数表本质是一个存放虚函数指针的指针数组,这个数组最后面放了一个nullptr
虚表结尾是0/nullptr

虚表存的是虚函数指针,不是虚函数,虚函数和普通函数是一样的,都是存在代码段,只是他的指针又存到了虚表中。
对象中存的不是虚表,存的是虚表指针。在vs2013平台下虚表存在代码段。

构造函数不能是虚函数,必须要有一个完整的对象,从对象的前4个字节中获取。

同一个类的对象的虚表是一样的。
如果一个函数不是虚函数,编译器在编译期会直接调用该函数。
如果一个函数是虚函数,编译器在编译期间不会直接调用该函数。

多继承方式虚表的构建过程:
与单继承类似+将派生类新增加的虚函数保持在第一张(第一个基类对应)的虚表的最后。

满足多态以后的函数调用,不是在编译时确定的,是运行起来以后到对象的中去找的。不满足多态的函数调用时编译时就确认好的。

静态绑定,静态多态:在编译时就可以确定函数的的行为 函数重载 模板
动态绑定,动态多态:在程序运行时确认具体调用哪个函数 虚函数

inline函数可以是虚函数吗?
不能,inline函数没有地址,无法把地址放到虚函数表中

静态成员可以是虚函数吗?
不能,因为静态成员函数没有this指针,使用类型::成员函数的调用方式无法访问虚函数表,所以静态成员函数无法放进虚函数表。

构造函数可以是虚函数吗?
不能,因为对象中的虚函数表指针是构造函数初始化阶段才初始化的。

对象访问普通函数快还是虚函数更快?
如果是普通对象,是一样快的。如果是指针对象或者是引用对象,则调用普通函数快,因为构造多态,运行时调用的虚函数需要在虚函数表中进行查找。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值