《C++ Primer Plus》学习笔记8
第13章 类继承
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
主要内容:
①is-a关系继承
②如何以公有的方式从一个类派生到另一个类
③保护访问
④构造函数成员初始化列表
⑤向上和向下强制转化
⑥虚拟成员函数
⑦早期(静态)联编和晚期(动态)联编
⑧抽象基类
⑨纯虚函数
⑩何时以及如何使用公有继承
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
1、一个简单的基类
从一个类派生出另一个类时候,原始类称为基类,继承类称为派生类
1)派生一个类
TableTennisClass class:
//将RatedPlayer类声明为从TableTennisClass类派生而来
class RatedPlayer: public TableTennisPlayer
{
……
}
Ratedplayer对象将具有以下特征:
①派生类对象存储了基类的数据成员(派生类继承了基类的实现)
②派生类对象可以使用基类的方法(派生类继承了基类的接口)
2)构造函数:访问权限的考虑
补充:默认构造函数要么没有参数,要么所有的参数都有默认值,如果没有定义任何构造函数,编译器将定义默认构造函数,让您能够创建对象,自动生成的默认构造函数的另一项功能是,调用基类的默认构造函数以及调用本身是对象的成员所属类的默认构造函数。
①派生类不能直接访问基类的私有成员,而必须通过基类方法进行访问,也就是说派生类构造函数必须使用基类构造函数。
所以说创建派生类对象时,程序首先创建基类对象,从概念上说,这意味着基类对象应当在程序进入派生类构造函数之前被创建。
②派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数
③派生类构造函数应初始化派生类新增的数据成员
④派生类对象过期时,程序将首先调用派生类析构函数,之后再调用基类析构函数。
补充:提供构造函数的动机之一是确保对象总能被正确地初始化,另外,如果类包含指针成员,则必须初始化这些成员
3)使用派生类
使用派生类程序必须要能够访问基类声明
2、派生类和基类之间的特殊关系
①派生类对象可以使用基类的方法,条件是方法不是私有的;
②基类指针可以在不进行显式类型转换的情况下指向派生类对象
③基类引用可以在不进行显式类型转换的情况下引用派生类对象。
④基类指针或引用只能用于调用基类方法,因此,不能使用rt或者pt来调用派生类的ResetRanking方法。
2、继承——is-a关系
补充:表示is-a关系的方式之一是,无须进行显式类型转换,基类指针就可以指向派生类对象,基类引用可以引用派生类对象。
派生类和基类之间的关系时基于C++继承的底层模型的,C++有3种继承方式,公有继承、保护继承、私有继承。
①is-a关系是派生类对象也是一个基类对象,可以对基类对象执行任何操作,也可以对派生类对象执行。全名叫做is-a-kind-of(是一种)的关系
②公有继承不能建立is-like-a关系,也就是说它不采用明喻;
③公有继承不建立is-implemented-as-a关系,(作为……来实现)例如可以使用数组来实现堆栈,但从Array类派生出Stack是不合适的,因为堆栈不是数组。
补充:什么不能被继承?构造函数不能被继承,也就是说创建派生类对象时,无需调用派生类的构造函数,不过,派生类构造函数通常使用成员初始化列表句法来调用基类构造函数,以创建派生对象的基类部分;析构函数也是不能被继承的,在释放对象时,程序将首先调用派生类的析构函数,之后调用基类的析构函数。
3、多态公有继承
实现方法:
①在派生类中重新定义基类的方法
②使用虚方法
4、使用虚函数注意事项
①在基类方法的声明中使用关键字virtual可使该方法在基类以及所有的派生类中是虚拟的
②如果使用指向对象的引用或指针来调用虚方法,程序将使用为对象类型定义的方法,而不使用为引用或指针类型定义的方法,这称为动态联编或者晚期联编。这样基类指针或引用可以指向派生类对象
③如果定义的类将被用作基类,则应将那些要在派生类中重新定义的类方法声明为虚拟。
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
1)构造函数:构造函数不能是虚函数,创建派生类对象时,将调用派生类的构造函数,而不是基类的构造函数,派生类的构造函数将使用基类的一个构造函数,这个顺序不用于继承机制,派生类不继承基类的构造函数,所以将类构造函数声明为虚拟的没有什么意义。
2)析构函数:析构函数应当是虚函数,除非类不用做基类。通常应给基类提供一个虚拟析构函数,即使它并不需要析构函数。当通过指向对象的基类指针或引用来删除派生对象时,程序将首先调用派生类的析构函数,之后调用基类的析构函数,而不仅仅是调用基类的析构函数。
3)友元:友元不能是虚函数,因为友元不是类成员,而只有成员才能是虚函数。故不能继承,如果由于这个原因引起了设计的问题,可以通过让友元函数使用虚拟成员函数来解决。
5、访问控制:protected
关键字protected与private相似,在类外只能用公有类成员来访问protected部分中的类成员,他们的区别只有在基类派生的类中才会表现出来,派生类的成员可以直接访问基类的保护成员,但不能直接访问基类的私有成员。所以,对于外部世界来说,保护成员的行为与私有成员相似,但对于派生类来说,保护成员的行为与公有成员相似。
派生类可以直接访问基类的保护成员,但只能通过基类的成员函数来访问私有成员。
最好对类数据成员采用私有访问控制,不要使用保护访问控制,同时通过基类方法使派生类能访问基类数据。
对于成员函数来说,保护访问控制很有用,它让派生类能够访问公众不能使用的内部函数。