继承和多态
C++三大特性:封装,继承,多态
封装:通过访问权限的控制(private,protect,public) 将成员变量变得不可见,
继承:一个类继承另外一个类的成员变量和成员函数
被继承的类:父类(基类)
继承的类:子类(派生类)
继承方式:
1、public:公有继承 public->public protected->protected private->private
1 | class Student:public Person //其中public代表继承权限 |
(1)父类的私有成员可以被继承,但是不可见
(2)子类可以调用父类的变量进行初始化,但这个函数只能在初始化列表中调用
1 | using CStrRef = const std::string &; |
(3)父类先构造,子类先析构
(4)继承中的名词遮蔽:父类与子类定义同名函数,子类对象只能调用的是子类函数,如果非要使用,要用域解析符
1 | s.Person::show(); |
2、proteced:保护继承 public->protected protected->protected private->private
1 | class Student:protected Person |
3、private:私有继承 public->private protected->private private->private
1 | class Student:private Person |
一般都用public,private几乎不使用
开闭原则:对修改关闭,对扩展开发
4、多继承
1 | class Derived:public Base1,public Base2 //以 , 隔开继承两个父类 |
父类构造顺序和构造函数调用顺序无关,和继承顺序有关
二义性问题:当两个父类都定义一个同名变量时,子类调用会出现定义不明确的问题
解决菱形继承的二义性问题:虚继承 virtual
1 | class Base1:virtual public ULtraBase |
虚继承指针->虚继承表(虚继承的类的变量相对于本类的偏移量)
5、组合
将要使用的类的对象,塞入当前类中
1 | class Base1 |
多态:
相同的代码,不同的表现形式
目的:提高代码的可扩用性
静多态:在编译期进程函数的地址绑定(早绑定) C多态:函数指针(回调) C++多态:函数重载
动多态:在运行期才真正确定函数的地址(迟绑定) C++:虚函数virtual
虚函数指针:虚函数表
1 | virtual void show() |
重写:子类函数和基类函数完全相同(返回值,函数名,参数列表)
只有重写的情况下,才能构成多态
重载,重写,名词遮蔽的概念一定要分清。
构成多态的要素:
1、基类指针指向子类对象
2、基类要有虚函数
3、子类重写父类虚函数
纯虚函数:
定函数的格式
抽象类:具有纯虚函数的类,不允许实例化对象
1 | //具有纯虚函数的类:抽象类:不允许实例化对象 |
1 | class Base |
虚析构函数的作用:
向上转型时,释放基类指针,不会调用子类析构,造成子类资源无法释放,基于这个原因,我们把父类析构函数定义为虚函数
1 | class Base |
类的内存空间分配
不同的对象应该具有不同的内存地址,空类占一个字节。
静态变量不占类型,函数不占,虚函数会有一个虚函数表占8个字节
基类和子类之间不能拼接空间
创建虚函数指针之后,编译器会自己进行优化内存,会使基类和子类的空间以及可以拼接的空间进行拼接
虚函数表是一个类一张表,虚函数指针指向表吗,表里可以储存多个虚函数
g++编译器内存存储顺序:1、有虚继承、虚函数:虚继承指针->子类变量->虚函数指针->基类变量
? 2、有虚函数,无虚继承:虚函数指针->基类变量->子类变量