1.类继承的意义:可重用的代码
面向对象编程的主要目的之一是提供可重用的代码。在项目开发时,重用已经经过充分测试的代码比重新编写代码好得多。
C语言中的可重用性:
C函数库中预定义、预编译的函数;
厂商也提供了专用的C库,提供标准C库没有的函数。
函数库的局限性在于,除非厂商提供了库函数的源代码,否则无法根据自己的特定需求,在原始库函数的基础上修改自己的程序;就算厂商提供了源代码,在修改程序时也将面临一定的风险。
C++中的可重用性:
很多厂商提供了类库。
类库由类声明和类实现组成,类中包含了数据表示和类方法,提供了比函数库更完整的程序包,往往一个类就可以用于管理某种资源。
类库一般以源代码的形式提供,因此可以对其进行修改。但是在C++中,有比修改代码更好的方法来扩展和修改类,即类的继承。
2.继承一个类
继承的语法:
class RatedPlayer : public TableTennisPlayer
{
...
};
派生类对象包含基类对象。
派生类对象存储了基类的数据成员(派生类对象继承了基类的实现);
派生类对象可以使用基类的方法(派生类对象继承了基类的接口)。
使用公有派生,基类的公有成员将成为派生类的公有成员;基类的私有成员也将成为派生类的一部分,但是只能通过基类的公有和保护方法访问。
派生类需要自己的构造函数;
派生类可以根据需要添加额外的数据成员和成员函数。
派生类必须给新成员(如果有的话)和继承的成员提供数据。
3.构造函数中访问权限的考虑
由于派生类不能直接访问基类的私有成员,必须通过基类的公有和保护方法访问。因此派生类构造函数必须调用基类构造函数,来给继承的基类的私有成员赋值。
创建派生类对象时,程序首先创建基类对象。所以,基类对象应该在程序进入派生类构造函数之前被创建,C++使用成员初始化列表来完成该项工作:
RatedPlayer::RatedPlayer(unsigned int r, const string & fn, const string & ln, bool ht) : TableTennisPlayer(fn, ln, ht)
{
rating = r;
}
如果不调用基类构造函数,程序将使用默认的基类构造函数,以保证基类对象先于派生类对象创建。
除非要使用默认构造函数,否则应显式调用正确的基类构造函数。
派生类构造函数的要点:
1.先创建基类对象
2.派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数
3.派生类构造函数应初始化派生类新增的数据成员
派生类对象过期时,先调用派生类析构函数,再调用基类析构函数。
成员初始化列表只能用于构造函数。除了虚基类外,类只能将值传递给相邻的基类,但后者可以使用相同的机制将信息传递给相邻的基类,依此类推。
4.基类和派生类的特殊关系
基类指针可以在不进行显式转换的情况下指向派生类对象;
基类引用可以在不进行显式转换的情况下引用派生类对象。
基类的指针和引用只能调用基类的方法。
不能将基类对象和地址赋给派生类引用和指针。
//base是基类,derived是派生类
derived derived1();
base & base1 = derived1; // 基类引用指向派生类对象
base * base2 = & derived1; // 基类指针引用派生类对象
base1.base_method(); // 基类引用调用基类方法
base2->base_method(); // 基类指针调用基类方法
当函数的形参为基类引用时,可以使用基类对象或派生类对象作为实参;
当函数的形参为基类指针时,可以使用基类地址或派生类地址作为实参;
void base_method(const base & bs); //形参为基类引用
base_method(base1); //实参为基类对象
base_method(derived1); //实参为派生类对象
void base_method1(const base * bs); //形参为基类指针
base_method1(&base1); //实参为基类地址
base