C++ 三大特性:继承、封装和多态

封装

分为public、private和protected

  1. 暴露public
    1)可内部调用和实例化调用

  2. 隐藏private
    1)只能由类成员(类内)和友元访问
    2)不可见,能继承来,但无法访问

  3. 受保护protected
    1)类内和继承类调用

public > protected > private

继承

  1. 访问方式
    1)基类成员在派生类中访问权限不能超过继承方式。
    2)基类成员如果小于派生类的权限,则维持原状。

  2. 构造顺序 构造顺序
    有基类先构造基类

  3. 析构函数 析构顺序
    1.调用自身的析构函数
    2.按照构造的逆序删除数据成员
    3.如果有父类,按照继承的逆序,删除父类

防止多态时,内存泄漏问题,基类应该定义虚析构函数,编译器会把基类和派生类的析构函数进行绑定,虽然名字不一样。

  1. C++ 内存结构

1)成员数据
1. 结构体第一个成员放到0偏移处
2. 虚表起始地址在数据成员前面
3. 基类按照继承顺序,放在派生类前面
4. 非静态成员按照声明顺序放在类对象中
5. 静态成员放在静态数据区。继承中,父类和子类共用一个空间

2)成员函数
1.静态成员
1)只能访问类的静态成员,不能访问非静态成员,无this指针
2)与普通成员一样,在程序编译的时候是静态绑定,调用效率没有差别

3)虚函数表

  1. 虚表是一个指针数组,其元素是虚函数的指针,每个元素对应一个虚函数的函数指针。在代码编译期间构造出来。

  2. 虚表是属于类的,而不是属于某个具体的对象。对象内部包含一个虚表的指针,来指向自己所使用的虚表。为了让每个包含虚表的类的对象都拥有一个虚表指针,编译器在类中添加了一个指针,*__vptr,用来指向虚表。这样,当类的对象在创建时便拥有了这个指针,且这个指针的值会自动被设置为指向类的虚表。

  3. 多继承派生类的未重写的虚函数放在第一个继承基类部分的虚函数表中。含有多个虚函数表指针。重写了,多个虚函数表都会改。未重写,会有二义性问题。

  4. 虚函数
    1)原理:包含虚函数的基类有虚函数列表,有一个指针指向虚函数列表。
    派生类也有一个虚函数列表,也有一个指针指向派生类的虚函数列表,当函数同名时,会覆盖掉基类函数
    根据运行时指针(引用)所指向对象的实际类型,查找函数入口地址。
    2)派生类中定义的虚函数必须与基类中的虚函数同名,参数的类型、顺序、参数的个数必须一一对应,函数的返回的类型也相同
    3)如果在类外定义虚函数,不需要加virtual关键字,类内声明时要加virtual关键字

C++内存结构:
在这里插入图片描述
链接:
https://blog.csdn.net/MOU_IT/article/details/89045103

当两个或多个基类中有同名的成员时,如果直接访问该成员,就会产生命名冲突,编译器不知道使用哪个基类的成员。

解决多重继承二义性的方法
加类限定
在派生类中定义同名成员覆盖。(隐藏属性)
参数不一定要相同
虚继承 (在继承方式前+virtual)

纯虚函数:
形式:virtual 函数类型 函数名 (参数表列) = 0;即在虚函数后面加=0;
在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。
带有纯虚函数的类为抽象类,无法实例化。

虚继承:
存在问题:

从不同途径继承来的同一基类,会在子类中存在多份拷贝。这将存在两个问题:第一,浪费存储空间;第二,存在二义性问题。
解决方法:

虚继承=》C++ 提供虚基类(virtual base class)的方法,使得在继承间接共同基类时只保留一份成员。方法如下:
3.虚继承—解决菱形继承的二义性和数据冗余的问题

多态

定义:“一个接口,多种方法”,实现接口的复用。
在这里插入图片描述
静态多态和动态多态的区别是函数地址是早绑定还是晚绑定的。

静态多态:
在编译期间就可以确定函数的调用地址。

动态多态:在运行时才确定函数的调用地址。
动态绑定条件:
1.只有指定为虚函数的成员函数才能进行动态绑定
2.必须通过基类类型的引用或者指针进行函数调用

链接:https://blog.csdn.net/xy913741894/article/details/52939323

继承中的调用关系

基类指针指向派生类对象

  1. 函数为非虚函数,调用基类定义的函数。
  2. 函数为虚函数,根据指针实际指向的对象类型确定。

派生类指针指向基类对象—不推荐使用,容易出现问题。
1.必须先做强制转型动作(explicit cast),而且调用派生类对象时,如果访问了父类不存在的成员变量,会发生越界访问
2. 父类的print()方法被隐藏了,要想使用父类的方法必须通过::。

重载、重写和重定义
1.重载 同一作用域
1)函数名相同
2)参数个数,类型或顺序有一个不同

2.重写 不同作用域,基类和派生类
1)即覆盖,子类重新定义父类的同名称和参数的虚函数
2)不重写则调用父类的

重定义 不同作用域,基类和派生类
1)隐藏,函数名相同即可,非虚函数都是重定义

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值