day5:
1 继承
基类(父类):已经定义好的一个类
派生类(子类):是一个新定义的类,该类继承了基类的成员
继承:新定义的类直接使用继承的父类的属性和方法的一种方式
派生类的定义格式:
class 新类名:继承方式 基类名
{
(继承自基类的成员)
子类新增的成员;
}
继承方式有三种:public protected private
public(公有访问限定符):可以被类中的成员函数、子类的函数和其友元 函数访问,也可以被该类的对象访问。
protected(保护访问限定符):可以被类中的成员函数、子类的函数和其 友元函数访问,但不能被该类的对象访问
private(私有访问限定符):只能由该类中的成员函数和其友元函数访问, 不能被子类函数访问,也不能被该类的对象访问
2 公有继承
子类的定义格式:
class 新类名:public 基类名
{
(继承自基类的成员)
子类新增的成员;
}
父类的公有成员公有继承到子类中,变成了子类的公有成员;
父类的保护成员公有继承到子类中,变成了子类的保护成员;
父类的私有成员公有继承到子类中,变成了子类不可访问;
3 保护继承
子类的定义格式:
class 新类名:protected 基类名
{
(继承自基类的成员)
子类新增的成员;
}
父类的公有成员保护继承到子类中,变成了子类的保护成员;
父类的保护成员保护继承到子类中,变成了子类的保护成员;
父类的私有成员保护继承到子类中,变成了子类不可访问;
4 私有继承
子类的定义格式:
class 新类名:private 基类名
{
(继承自基类的成员)
子类新增的成员;
}
父类的公有成员私有继承到子类中,变成了子类的私有成员;
父类的保护成员私有继承到子类中,变成了子类的私有成员;
父类的私有成员私有继承到子类中,变成了子类不可访问;
总结:
访问方式
public protected private
继承方式
public继承 公有成员 保护成员 不可访问
protected继承 保护成员 保护成员 不可访问
private继承 私有成员 私有成员 不可访问
5 多层继承
是垂直继承,是C继承了B,B继承了A ,那么C 间接的继承了A
多层继承的子类定义对象时,对象的构造顺序:先基类再子类
对象的析构顺序:先子类再基类
注意: 如果基类中有成员变量,那么可以通过子类的构造函数给基类传参,用参数列 表方式传递
sub(int a,int b):base(a),x(b)--->子类构造函数
sub1(int a,int b,int c):sub(a,b),y(c)-->孙子类构造函数
6 多重继承
是指一个子类继承自多个父类
class 子类名:继承方式 父类1,继承方式 父类2
{
(继承自父类1和父类2的成员)
子类新增成员;
}
多重继承的子类定义对象时,
对象的构造顺序:先基类(多个基类从左到右构造)再子类
对象的析构顺序:先子类再基类(多个基类从右到左)
多重继承如何传参?
多重继承的子类定义的对象,初始化时会通过子类的构造函数给基类传参, 用参数列表的方式传
7 多层多重继承引发的二义性
有三种解决方法:1 加作用域
2 同名覆盖
3 虚继承--》菱形继承
虚继承表示:在继承方式的位置处virtual
虚继承:指的是在多个派生类中声明继承的基类是虚基类,那么多个派生类就会 共同维护一份基类成员的拷贝,从而解决菱形继承引发的二义性。
注:虚继承的目的是让子类做出声明,承诺愿意共享它的基类,这个被共享的基 类称为虚基类。
c++使用虚拟继承,解决从不同路径继承而来的同名数据成员在内存中由不同的拷贝造成的数据不一致问题,将共同基类设置为虚基类, 这时从不同路径继承而来的数据在内存中就只有一份拷贝,同一个函数名也只有一个映射。
day5作业:
1 编写一个学生基类,类中有属性和行为函数;用基类派生出大学生类、中学生类、小学生类,然后分别用这些类定义对象,测试类中的成员函数
day5:
1 什么是多态?
多态:“多种状态”,指的是相同对象收到不同消息(静态多态),或者不同对象收到相同消息时产生的不同行为(结果不同)
本质:调用同一接口,表现出不同的结果
静态多态:指的是相同对象收到不同消息,产生不同的行为(结果不同)
--》函数重载
int add(int a,int b)
{return a+b;}--->30
int add(int a,int b)
{return a+b+10;}-->40
float add(float a, float b)
动态多态:指的是不同对象收到相同消息,产生不同的行为(结果不同)
--》虚函数
虚函数?
2 多态的做用?
作用:整个类家族对外提供统一的接口,方便用户调用,提高代码的可扩展性和可维 护性
3 c++的多态是如何实现的?
c++的多态是通过虚函数来实现的。
什么函数是虚函数呢?在类的成员函数前加virtual后,该函数就是虚函数了。
virtual使用限制:1 virtual只能修饰类的成员函数(不能修饰类外的普通函数),修 饰类中的普通成员函数和析构函数
2 不能修饰构造函数
3 不能修饰静态成员函数
被virtual修饰的基类的函数可以在子类中可以被重写。
重写:就是重新定义。子类继承了基类的虚函数,子类可以对继承过来的该虚函数 实现函数重写。
函数重写就是子类的该函数函数头和基类的函数头一致,函数体和基类的函 数体不一样。
实现c++动态多态的三个条件:
1 必须要有继承
2 在派生类中要重写继承自基类的虚函数
3 通过基类的指针或引用指向派生类对象,通过指针或引用调用虚函数
base *p=new sub;
p->func();
1 多态的引入
4 虚函数工作原理
含有虚函数的类,类的大小会比不含虚函数的类的大小多出4个字节,多出的这4个 字节存放的是虚函数表指针。
虚函数表指针:指向类中虚函数表的指针
虚函数表:存放当前类中所有虚函数地址的一张表(存放顺序与函数在类中的声明顺 序一致)
c++编译器会给含有虚函数的类编译生成一张虚函数表,并生成一个虚函表指针指向该虚函数表。当我们用该类定义对象时,对象的地址自动赋给虚函数表指针,所以对象可以直接调用虚函数;当我们用基类的指针指向派生类对象时,基类指针的值就是派生类对象的地址,所以基类指针p可以通过vptr访问子类对象的虚函数(重写后的)
5 函数重载 函数重写 函数隐藏的区别
函数隐藏(同名覆盖):指的是成员函数隐藏
A 不在同一作用域(分别位于基类和派生类中)
B 函数的名相同,函数名前没有virtual
C 返回值可相同,也可不同
D 参数可以相同也可以不同,要分情况
函数重载:是指函数名相同,参数不同(个数、类型),与返回值无关
A 相同的范围(在同一作用域中)
B 函数名相同
C 参数不同
D 没有virtual关键字
E 返回值可以相同,也可不同
函数重写:成员函数覆盖(指派生类重新定义继承自基类的虚函数)
A 不同的作用域(分别位于基类和派生类中)
B 函数名相同
C 参数相同
D 函数体不同
E 基类函数前必须有virtual,子类的该函数可有可无
F 返回值类型相同
G 重写函数的访问限定符则可以和基类的不同
6 抽象类和纯虚函数
纯虚函数:是类中的一个虚函数,只有声明没有函数体,函数体为0
抽象类:包含纯虚函数的类
注意:抽象类无法实例化对象
7 虚析构函数
基类的指针指向派生类对象,当基类的指针要释放内存的时候(delete p),系统只会调用基类的析构函数去析构子类对象中的基类部分,不会调用子类的析构函数去析构子类新增部分。那么子类成员所占的内存空间就不会被释放,会造成内存泄漏和堆区空间浪费。
那么如何解决这个问题呢?用虚析构函数,在基类的析构函数前加virtual关键字
注:只要将基类的析构函数设置成虚析构函数,那么就可以通过释放基类的指针 去调用子类的析构函数。所以,建议:若要将一个类作为基类,那么就把该类的析构函数设置成虚析构函数。