4 多态

4 多态

#知识点总结#

4.1多态

如果子类定义了与父类中原型相同的函数会发生什么?
赋值兼容性原则遇上函数重写
–父类中被重写的函数依然会继承给子类
–默认情况下子类中重写的函数将隐藏父类中的函数
–通过作用域分辨符::可以访问到父类中被隐藏的函数

Parent *p = NULL;
p = &child;
p->print(); //会调用父类的函数,为什么?

C/C++是静态编译型语言,在编译时,编译器自动根据指针的类型判断指向的是一个什么样的对象。
1、在编译此函数的时,编译器不可能知道指针 p 究竟指向了什么。
2、编译器没有理由报错。
3、于是,编译器认为最安全的做法是编译到父类的print函数,因为父类和子类肯定都有相同的print函数。

面向对象新需求:
根据实际的对象类型来判断重写函数的调用:
–如果父类指针指向的是父类对象则调用父类中定义的函数
–如果父类指针指向的是子类对象则调用子类中定义的重写函数

解决方案
C++中通过virtual关键字对多态进行支持
使用virtual声明的函数被重写后即可展现多态特性

多态工程意义
面向对象3大概念:
封装:突破了C语言函数的概念。。用类做函数参数的时候,可以使用对象的属性和方法
继承:代码复用
多态:多态可以使用未来,写了一个框架,可以调用后来人写的代码的能力

多态成立的条件
C语言 间接赋值 是指针存在的最大意义
间接赋值成立的3个条件
1 定义两个变量
2 建立关联
3 *p 在被调用函数中去间接地修改实参的值

多态成立的三个条件
1 要有继承
2 要有虚函数重写 virtual
3 要有父类指针(父类引用)指向子类对象
多态是设计模式的基础,多态是框架的基础

多态的理论基础
1、联编是指一个程序模块、代码之间互相关联的过程。
2、静态联编(static binding),是程序的匹配、连接在编译阶段实现,也称为早期匹配。
重载函数使用静态联编。
3、动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编(迟绑定)。
switch 语句和 if 语句是动态联编的例子。在运行的时候,根据具体的对象(具体 的类型),执行不同对象的函数,表现成多态。

4.2多态相关面试题

面试题1:请谈谈你对多态的理解
多态的实现效果
多态:同样的调用语句有多种不同的表现形态;同样的函数在不同的子类和父类中穿梭的时候表现出不同的形态。
多态实现的三个条件
有继承、有virtual重写、有父类指针(引用)指向子类对象。
多态的C++实现
virtual关键字,告诉编译器这个函数要支持多态;不是根据指针类型判断如何调用;而是要根据指针所指向的实际对象类型来判断如何调用
多态的理论基础
动态联编PK静态联编。根据实际的对象类型来判断重写函数的调用。
多态的重要意义
设计模式的基础 是框架的基石。
实现多态的理论基础
函数指针做函数参数
C函数指针是C++至高无上的荣耀。C函数指针一般有两种用法(正、反)。
多态原理探究
与面试官展开讨论,多态的原理是这么实现的,是C++编译器提前布局vptr指针,然后找到函数的入口地址来进行动态的迟绑定。

面试题2:谈谈C++编译器是如何实现多态
面试题3:谈谈你对重写,重载理解

函数重载
必须在同一个类中进行
子类无法重载父类的函数,父类同名函数将被名称覆盖
重载是在编译期间根据参数类型和个数决定函数调用
函数重写
必须发生于父类与子类之间
并且父类与子类中的函数必须有完全相同的原型
使用virtual声明之后能够产生多态(如果不使用virtual,那叫重定义)
多态是在运行期间根据具体对象的类型决定函数调用

问题1:child对象继承父类对象的func,请问这句话能运行吗?why
c.func(); //因为名称覆盖,C++编译器不会去父类中寻找0个参数的func函数,只会在子类中找func函数。
//1子类里面的func无法重载父类里面的func
//2当父类和子类有相同的函数名、变量名出现,发生名称覆盖(子类的函数名,覆盖了父类的函数名。)
//3//c.Parent::func(); 只能使用域作用符访问父类的func()

面试题4:类的每个成员函数是否可以都声明为虚函数,为什么
可以,但不建议,运行效率问题
通过虚函数表指针VPTR调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数。而普通成员函数是在编译时就确定了调用的函数。在效率上,虚函数的效率要低很多。
出于效率考虑,没有必要将所有成员函数都声明为虚函数。

面试题5:构造函数中调用虚函数能实现多态吗?为什么?
面试题6:虚函数表指针(VPTR)被编译器初始化的过程,你是如何理解的?
面试题7:父类的构造函数中调用虚函数,能发生多态吗?

回答以上三个
对象在创建时,由编译器对VPTR指针进行初始化
只有当对象的构造完全结束后VPTR的指向才最终确定

子类vptr指针初始化是分步的
子类对象构造时,在父类构造函数中调用虚函数,无法实现多态
面试题8:为什么要定义虚析构函数?
构造函数不能是虚函数。建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数
析构函数可以是虚的。虚析构函数用于指引 delete 运算符正确析构动态对象
想通过父类指针 把所有的子类对象的析构函数都执行一遍
想通过父类指针 释放所有的子类资源
virtual ~A()
直接通过子类对象释放资源不需要写virtual

4.3多态原理探究

当类中声明虚函数时,编译器会在类中生成一个虚函数表
虚函数表是一个存储类成员函数指针的数据结构
虚函数表是由编译器自动生成与维护的
virtual成员函数会被编译器放入虚函数表中
当存在虚函数时,每个对象中都有一个指向虚函数表的指针(C++编译器给父类对象、子类对象提前布局vptr指针;当进行howToPrint(Parent *base)函数时,C++编译器不需要区分子类对象或者父类对象,只需要在base指针中,找vptr指针即可。)
vptr一般作为类对象的第一个成员

C++编译器,执行HowToPrint函数,不需要区分是子类对象还是父类对象

如何证明vptr指针的存在:sizeof()
加上virtual关键字(无论多少个),c++编译器只会增加一个指向虚函数表的指针

父类指针和子类指针的步长
1)铁律1:指针也只一种数据类型,C++类对象的指针p++/–,仍然可用。
2)指针运算是按照指针所指的类型进行的。
p++《=》p=p+1 //p = (unsigned int)basep + sizeof(*p) 步长。
3)结论:父类p++与子类p++步长不同;不要混搭,不要用父类指针++方式操作子类对象数组。否则会出现coredump。
多态是用父类指针指向子类对象 和 父类指针步长++,是两个不同的概念。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值