继承与多态 【C++复习】

24 篇文章 2 订阅

继承与多态

---记录基本概念与编程要点


一些概念:

1、继承:在基类的基础上加以扩展,增加功能,这样产生的类,叫派生类
2、多态:发生在不同层次的类中,以及同一类中,同名函数之间的关系问题
  1. 编译时期的多态(早绑定):指在编译时期就确定了函数的执行时机、调用关系,如函数的重载
  2. 运行时期的多态(晚绑定):是以虚函数+公有继承为基础,在程序运行时才能确定的调用关系
3、struct和class的区别:
  • struct默认public,struct继承时,缺省情况下是公有继承
  • class默认private,class继承时,缺省情况下是私有继承

4、编译链接时候,会在派生类中对象中,先构建一个基类对象(隐藏对象),再去构建派生类对象的数据成员。

5、继承时的访问权限问题不论是哪种继承方式,在派生类中只有非自己的私有成员不可访问。但,派生类中的基类具名对象的保护和私有无法访问,这和继承是不同的
隐藏:在派生类中会对基类中的同名成员进行隐藏,访问遵循就近原则(也是原因)。
友元类:可以访问类中的所有成员。

6、赋值兼容规则:(公有继承是前提)

  1. 派生类对象可以赋值给基类对象
  2. 派生类对象的指针可以赋值给基类的指针
  3. 派生类对象可以初始化基类对象的引用

7、转型规则:

  • 向上转型派生类对象转为基类对象,使用dynamic_cast<type_id>(),可能会发生切片现象
  • 向下转型:基类对象转为派生类对象,可以使用强制转换,但并不安全

8、在继承关系中,构造函数与析构函数具有继承性,拷贝构造函数不具有继承性,赋值函数也不具有继承性,必须明确调用。

9、C++类对象的初始化顺序:父类构造 先于 成员类对象构造 先于 自身构造

  • 初始化列表不一定是构建的顺序,其中成员变量的初始化与声明顺序有关,构造函数的调用顺序是类派生表中的顺序
  • 析构函数的析构顺序与构造函数构造顺序相反

多态与虚函数

1、只有类的成员函数才可以被定义为虚函数,虚函数定义:

  • virtual 返回类型 函数名(参数列表){ 函数体 } //类外定义时可以不加virtual关键字,但类内的声明一定要写
    当一个类的成员函数被定义为虚函数,则其所有的派生类中,该函数始终保持虚函数特征。
    运行时多态的发生条件
  • public 继承,派生类对于基类一定要是公有继承的
  • 基类中写有虚函数
  • 使用指针或者引用调用虚函数

满足以上三点,才能够是动多态;三者缺一时,就是静多态
派生类中的虚函数与基类中的虚函数必须是同名、同参数列表、同返回类型,否则被认为是重载(同名函数的隐藏)【若为基类中虚函数返回基类指针,派生类中虚函数返回派生类指针也可以视作是多态】
静态函数、友元函数、内联函数不可作为虚函数,因为需要有继承关系,所以,所有的构造函数也不会是虚函数,但析构函数可以是虚函数(其派生类的析构函数也会成为虚函数),这样,当基类指针指向派生类对象,动态地去创建一个对象,释放时,就可以做到联级析构。

构造函数不会是虚函数的原因:1.首先,有虚函数的类有一个虚表,虚表地址要存储在类对象的内存空间中,但是构造函数就是来构造类对象的,还没构造,何来内存空间存储,不形成逻辑闭环。2.虚函数作用于多态发生函数的调用,而构造函数呢是为了构造类对象而调用的,不会是通过基类的指针或引用来进行调用,所以,他也没必要是虚函数。3.最后,虚表是在构造函数调用之后才建立的,因而构造函数不能为虚函数。

构造函数会set虚函数表,析构函数会reset虚函数表。

派生类的vftable先去继承基类的vftable,再检查派生类虚函数进行检查覆盖

若类中有虚函数,则应该还有一个虚函数指针(4字节)[派生类对象构造的时候,先构建基类对象,此时虚函数表指针指向基类的vftable,再构造派生类对象,之后修改虚函数表指针的指向(若派生类中重写有虚函数)]。

多态的发生

  • 对象名.方法() :静态联编,调用的是对象类型的方法(不论virtual或普通),在编译时期就确定了
  • 对象引用->方法(): 动态联编,则调用对应虚函数表中的virtual方法,在运行时查虚表来确定

静态联编在编译时要确定的东西:①类型 、②可访问属性 、③函数参数的默认值,而动态联编在调用方法时,也是在编译时先确定以上三者,但调用时会动态地确定调用的方法。

2、虚继承:解决C++多重继承的问题:1.浪费存储空间,2.存在二义性问题(如菱形继承)
采用虚继承则会在派生类中有一个虚基表指针,指向虚基表(实际上是存放相对偏移量用来寻找虚基类)
虚表在构造函数之前写入

纯虚函数:指被标明为不具体实现的虚拟函数成员,其实现依赖于不同的派生类,定义方式:

  • virtual 返回类型 函数名(参数列表)=0;
    本质是将虚函数表中对应的表项赋值为0,也就是指向了一个不存在的函数,因此,该类不能够实例化对象,在其派生类中,除非重写了此函数,否则也将无法实例化对象

虚函数和纯虚函数的定义中不能有static关键字:因为静态关键字是在编译前期进行确定的,而虚函数和纯虚函数作用于多态,发生在执行时期进行动态绑定,这二者是相互违背的

拷贝构造函数的参数是引用&,若为值传递,则将出现无无限递归。

抽象类:具有纯虚函数的类,即抽象类。——>该类不能用来定义对象,但可以定义指针。他的存在是为了表示现实世界中的抽象事物。(一般将构造函数写为protected)
当类中的所有方法均为纯虚函数时,且无数据成员,我们将其称之为接口。
抽象类不能用作参数类型、函数返回值类型或显式转换的类型。

3、对象析构问题
系统如何得知我们在程序中是创建了一个还是10个,还是n个对象呢?(上越界标志之下4字节存放个数信息)。
当用new创建对象时,free(),delete()都可以释放对象,但是free()不会回收对象可能持有的资源,但是delete()会。

比如:
①当此类中为未重写析构函数时:

//第一种情况
Object* op=new Object(10);
delete op;//ok
free(op);//ok
delete []op;//ok

//第二种情况
Object* op1=new Object[10];
delete op;//ok
free(op);//ok
delete []op;//ok

②当此类中重写有析构函数时:

//第一种
Object *op=new Object(10);
delete op;//ok
free(op);//ok
delete [] op;//error

//第二种
Object* op1=new Object[10];
delete op;//error,读取头部信息失败,出错
free(op);//error,不能全部释放
delete []op;//ok,向上抬4字节,看到数量信息10,向下析构10个大小空间
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值