作者:520sweet跃_322 | 来源:互联网 | 2023-08-12 16:33
都知道三大特性是封装继承和多态
封装:
封装其实是三个特性里面比较概念化的东西,也最容易理解
官方一点的说法就是能够避免很好保护类中的数据,从而保证数据安全,因为仅向外面暴露访问、交互接口,而把具体的细节隐藏起来。
这里注意一点,class成员默认访问权限和struct有什么不同?
class:private struct:public
继承:
继承的话涉及的知识点就比较多了
首先是他的定义一定要理解清楚:让某种类型对象获得另⼀个类型对象的属性和⽅法,它可以使⽤现有类的所有功能,并在⽆需重新编写原来的类的情况下对这些功能进⾏扩展
将⼈定义为⼀个抽象类,拥有姓名、性别、年龄等公共属性,吃饭、睡觉等公共⽅法,在定 义⼀个具体的⼈时,就可以继承这个抽象类,既保留了公共属性和⽅法,也可以在此基础上 扩展跳舞、唱歌等特有⽅法。
继承这里还存在一个“切片”的行为,官方一点就是派生类对象可以赋值给基类的对象 / 基类的指针 / 基类的引用,注意:基类对象不能赋值给派生类对象,基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的
其次继承中的子类成员将屏蔽父类对同名成员的直接访问(变量和函数)
还有一些小知识点:
1.友元关系不能继承
2.基类定义了static静态成员,则整个继承体系里面只有一个这样的成员,无论派生了多少个子类
最后就是菱形继承:
这里通过面试问题来讲一讲
1. 什么是菱形继承?菱形继承的问题是什么?
一个子类有两个或以上直接父类时称这个继承关系为多继承,菱形继承是多继承的一种特殊情况。菱形继承有数据冗余和二义性的问题
2. 什么是菱形虚拟继承?如何解决数据冗余和二义性的
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。虚拟继承通过使用虚基表和两个虚基表指针,利用虚基表中存的偏移量,用指针来标识会发生冗余和二义性的数据
3. 继承和组合的区别?什么时候用继承?什么时候用组合?
public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高
实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用继承,可以用组合,就用组合。
多态:
这里首先要提出的就是如何理解多态?
同⼀事物表现出不同事物的能⼒,即向不同对象发送同⼀消息,不同的对象在接收时会产⽣不同的⾏为(重载实现编译时多态,虚函数实现运⾏时多态)允许你将⽗对象设置成为和⼀个或更多的他的⼦对象相等的技术,赋值之后,⽗对象就可以根据当前赋值给它的⼦对象的特性以不同的⽅式运作
构成多态的两个条件
1. 必须通过基类的指针或者引用调用虚函数
2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写
这里还应该知道虚函数重写的两个例外1.协变2.析构函数的重写
多态的原理需要重点理解一下
虚函数表
/ /这里常考一道笔试题:sizeof(Base)是多少?
class Base
{
public:virtual void Func1(){cout <<"Func1()" <private:int _b = 1;
};
除了_b成员,还多一个__vfptr放在对象的前面(注意有些平台可能会放到对象的最后面,这个跟平台有关),对象中的这个指针我们叫做虚函数表指针(v代 表virtual,f代表function)。 一个含有虚函数的类中都至少都有一个虚函数表指针,因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表。
对于一般的多态,总结一下一个派生类虚表生成
a.先将基类中的虚表内容拷贝一份到派生类虚表中
b.如果派生 类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数
c.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。
满足多态以后的函数调用,不是在编译时确定的,是运行起来以后到对象的中取找的。
不满足多态的函数调用时编译时确认好的。
最后说几个辨析问答题
静态成员可以是虚函数吗?no
析构函数可以是虚函数吗?yes
构造函数可以是虚函数吗?no
对象访问普通函数快还是虚函数快?构成多态的话虚函数会慢,否则一样
虚函数表是在什么阶段生成的?存在哪里?编译阶段形成虚函数表,存在代码区
什么是抽象类?抽象类强制重写了虚函数,另外抽象类体现出了接口继承关系。