C++学习笔记之继承和多态

在C++中类和类的基本关系有:代理、组合、继承
代理:可以从容器适配器中可以体现,就是一个类是另一个类的接口,通过这个类,可以访问另一个类的方法。
组合:就是一个类是另一类的成员,用一个类定义的对象,作为另一个类的成员变量
继承:简单的说,一个类拥有另一个类的成员或方法

现在就来介绍一下继承
继承方式:
在这里插入图片描述
派生类的内存布局:
派生类不仅有自己的成员还直接或间接地继承了基类的成员,最重要的是派生类还继承了基类的作用域,所以派生类可定义和基类同名的成员变量。
内存布局中应该先是基类的成员变量之后,再试派生类的成员变量。

基类和派生类的同名的方法有三种关系:
重载:函数名相同,参数列表不同,但作用域相同
隐藏:基类和派生类中的同名成员方法,不考虑返回值和参数列表,用派生类对象
调用的时候,永远调用的是派生类自己的方法,把基类的方法名给隐藏掉了
覆盖:基类和派生类的结构中,有这样的函数,返回值相同,函数名也相同,参数列表
也相同,而且基类的这个函数还是virtual虚函数, 叫做覆盖关系

从下到上的的结构:
在继承的结构中,允许将一个派生类对象赋值给基类对象,也可以将一个派生类对象的地址赋值给一个基类的指针或引用。当返过来就不行了,因为一个派生类对象不仅有自己的成员还有基类的成员,可以将基类的成员赋值给一个基类的对象,但不能将一个基类对象赋值给一个派生类对象,如果成立的话,就算把基类的成员赋值给派生类,那派生类的成员从哪里来,很容易出现访问越界的问题。所以编译器将这种赋值视为一种错误。

动态绑定和静态绑定:
对象的静态类型:对象声明的时候采用的类型,在编译时期就能够确定下来
对象的动态类型:在运行的时候才能决定的类型。
动态类型是可以更改的,而静态类型是不能够更改的
动态绑定绑定的是动态类型,只有在运行的时候才会确定下来。比如一个基类的指针或引用指向一个派生类的对象,这时只有在运行的时候才会知道到底资格指针指向的是基类对象还是派生类对象,因为从编译器的角度只能确定这个指针或引用的类型是基类的类型。是RTTI类型。
静态绑定绑定的是静态类型,不能过更改。在编译时期就能确定,就是编译时期的类型
注意:
在覆盖方法中添加默认值没有用,因为它调用都是编译时就能识别的是静态的类型,由指针或引用的类型决定

什么是多态:
多态在C++里分为静态多态和动态多态:
静态多态:就是能在编译的时候就能确定的,如函数重载和模板
动态多态:在运行的时候才能明确,如虚函数,通过基类的指针或引用指向派生类对象,通过指针来调用派生类和基类的同名方法,基类指针指向那个基类对象,就调用那个派生类的方法。这是通过虚函数的虚函数表来实现的。

虚函数:
如果基类中有一个成员方法用virtual修饰的话,这个函数就是一个虚函数,它的派生类中继承的这个方法也将会是一个虚函数。
如果一个类有一个虚函数,将会产生一个虚函数表(保存的是虚函数地址和类型字符串),在构造这个类的对象的时候,不仅会给成员变量赋初值,还会定义一个虚函数表指针vfptr给这个变量,这个虚函数表指针的初始化是在构造函数的栈帧开辟之后就进行了,也就是说,在{和第一行代码之前就隐式的进行初始化,可以理解为这个指针是这个对象的一个成员变量,在内存分布中,首地址存放的就是这个vfptr,指向类的虚函数表(在堆上)。派生类的vfptr是从基类继承来的,每次基类构造函数给这个vfptr赋初值,然后派生类的构造函数在将自己vftable的地址进行覆盖。
含有纯虚函数的类叫做抽象类,不能实例化对象,当能定义指针或引用。

发生动态绑定的条件:
①类具有虚函数表
②使用的指针或引用访问虚函数

多重继承
一个派生类继承了多个基类:
引发的问题:
在菱形结构上,派生类中会具有很多重复的数据,浪费了大量的内存。
解决方法:
通过虚继承的方式。
被虚继承的类就是虚基类,通过virtual修饰。谁的数据重复了,所有从他继承的数据的类就要虚继承。
虚继承会产生vbtable(记录基类数据在派生类对象的内存分布的偏移量),和vbptr(指向vbtable)
用vbptr来取代之前基类数据在内存中的位置,在通过vbptr指向的vbtable来访问基类的数据。
基类的数据都顺移到派生类的内存最后。

几个问题:
1、是不是每次调用虚函数都会发生动态绑定?
不是,当用对象来访问虚函数的时候就不会发生动态绑定。对象在编译时期就已经将类型确定好了。运行的时候也不会发生改变。

2、析构函数能不能成为虚函数?什么时候需要把析构函数写成虚析构函数?
可以;用基类指针指向堆上new出来的派生类对象的时候,否则delete基类指针
的时候,派生类的析构函数无法调用,只能调用基类的析构函数

3、基类的作用是什么?
1、把派生类的公共属性写在基类里面,实现代码的复用。
2、保留公共的接口,不需要实现,等待派生类进行重写,能够使用多态机制

4、为什么派生类里面只有派生类中的vfptr而没有基类的vfptr?
因为如果保存基类的vfptr的话,这个vfptr将没有什么作用,如果一个派生类有多重继承的基类,那么这个派生类将会保存其所有多层基类的vfptr。这样浪费了大量的空间,而这些vfptr实质上没有什么作用。

5、基类的指针一定指向派生类的起始位置吗?
基类的指针不一定指向派生类的起始位置,指向的永远是基类数据的起始地址,虚继承中就不是在起始位置。

6、为什么要把基类设计为抽象类?
因为不希望实例化基类,只希望基类把派生类的公共属性集合在一起,实现代码的复用,提供公共的接口,给派生类来实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值