面向对象之---封装、继承、多态

tech2023-02-01  108

在学习C++时,我们都知道C++三大特性:封装、继承、多态 今天来整体进行复习一下: 首先来看封装: 封装的概念:将数据和数据的方法进行有机结合,隐藏对象中的属性和实现细节,仅对外公开接口和对象进行交互—例如电脑的充电口 封装的实现:通过类将对象的属性和接口进行有机结合,然后通过访问权限决定各个类成员的访问权限。public、protected、private

接下来理解一下继承: 继承的概念:继承就是为了实现代码的复用以及体现C++面向对象程序设计的层次结构 继承的作用:

起到代码复用,子类可以继承父类已有的特性实现多态在子类中也可以实现子类自己的特性

前边提到了封装的时候进行了类成员的访问权限设定,在继承体系中,对于基类不同的访问权限,当子类不同的继承方式进行继承会对成员的访问权限进行修改,主要体现如下表:

基类成员访问权限public继承protected继承private继承publicpublicprotectedprivateprotectedprotectedprotectedprivateprivateprivateprivateprivate

如果在继承体系中,子类是对父类的public继承,则有以下特性:

子类对象可以 直接赋值给基类,反之则不行基类的指针和引用可以引用子类子类的指针不能直接指向基类,但是可以通过强制类型转化让代码更安全

在继承体系中还有一个同名隐藏的概念值得注意理解: 何为同名隐藏:指的是在继承体系中,子类和基类拥有相同名称的成员 分类:

成员变量:与子类中成员变量是否和基类中成员变量类型是否相同无关成员函数:与子类和基类中成员函数的原型是否相同无关,是否为虚函数也无关

对于同名隐藏的体现: 出现同名隐藏后,使用子类调用继承体系中相同名称的成员优先调用的是子类自己的成员,基类成员调用不到,相当于子类将基类的成员隐藏管理

如何解决这种现象:

在继承体系中如果不是为了实现多态,尽量避免同名如果定义了相同名称的成员,想要通过子类访问基类的成员,就加上作用域限定符::基类成员

在继承体系中的构造函数和析构函数的调用:

构造函数:在子类中,先调用基类的构造函数,再调用自己的析构函数:在子类中,先调用子类的析构函数,再调用基类的析构函数

继承中最重要的一个概念:菱形继承!!! 菱形继承就是单继承和多继承的结合,那么菱形继承存在一个很大的问题就是会造成数据二义性和冗余性,解决办法:

表面解决:在访问时加上作用域限定符,显式告诉编译器,这次访问需要访问哪个基类继承下来的成员本质解决:在子类中,将继承基类的成员只存储一份,引入了菱形的虚拟继承,创建一个虚基表,虚基表中存放的是一个偏移量,一个当前位置相对于公共父类成员的偏移量。这样就可以通过偏移量来找到公共父类成员的位置并对其进行操作。

接下来讨论另一个重要的特性:多态

什么是多态?(可以从它的概念、分类、实现条件和原理这三个方面来说) 何为多态 :就是同一事物在不同的场景体现出来的不同的状态 多态的分类:

静态多态:静态多态就是用重载和模板来实现动态多态:就是使用继承和虚函数来进行实现的,也是我们最常研究的

实现多态的条件:

必须在继承的体系下基类中必须有虚函数,子类必须对基类中的虚函数进行重写虚函数的调用:必须通过基类的指针或者引用来调用虚函数

何为重写:

一定是子类对基类的虚函数进行重写,要被重写的函数一定是虚函数要形成重写,子类和基类的函数原型必须完全一致,有两个例外:协变----如果基类返回的是基类对象的指针或引用,同时子类也返回的是子类对象的指针或者引用;析构函数----基类和子类的析构函数名字可以不同

多态实现原理:

对象模型:如果一个类中包含虚表指针,则编译器回给该对象模型增加4个字节,用来存放虚表的地址,4个字节在对象的起始位置虚表(虚表的构建过程):对于基类虚表的构造:按照虚函数在类中声明的先后次序,依次添加到虚表中,对于子类虚表的构造过程:先将基类虚表中的内容拷贝一份,放到子类虚表中;如果子类重写了某个类的虚函数,则用子类的虚函数替换虚表中相同位置偏移量位置的基类虚函数;对于子类新增加的虚函数,放置在虚表的最后虚函数调用原理:虚函数的调用与普通函数调用的区别:普通函数直接调用。虚函数先在虚表中找前四个字节获取虚表地址,传递this指针,从虚表中获取虚函数的入口地址,对虚函数进行调用以上步骤讲解完毕后,可以详细理解多态的实现原理:也就是说在编译阶段并没有明确指出要调用哪个虚函数,而是将函数调用的确认时机推迟到代码运行阶段,判断多态的条件是否满足,通过基类的指针或者引用调用虚函数,通过指针找到虚表的地址,传递this指针和参数,根据虚表地址找到虚函数的地址,对虚函数进行调用
最新回复(0)