目录
类继承的概念
面向对象编程的主要目的之一是提供可重用的代码,C++相比C语言提供了更高层次的重用性,许多厂商提供了程序的类库,类库由类声明和实现构成。因为类组合了数据表示和类方法,因此提供了比C函数库更加完整的程序包。通常,类库是以源码的方式提供的,这意味着可以对其进行修改,以满足需求。但C++提供了比修改源码更好的方法来扩展和修改类,这种方法叫类继承,它能够从已有的类派生出新的类,派生类继承了原有类(基类)的特征,包括方法。
当然,也可以通过复制原始类代码,再对其进行修改。但继承机制只需提供新特性,甚至不需要访问源代码就可以派生出类。因此,如果购买的类库只提供了类方法的头文件和编译后的代码,仍可以使用库中的类派生出新的类。另外,自己编写的类也可以在不公开实现的情况下将它分发给其他人,同时允许他们在类中添加新特性。
派生类和基类
从一个类派生出另一个类时,原始类称为基类,继承类称为派生类。
为说明继承,首先需要一个基类,现有一个基类,数据成员部分定义了学生的各科成绩以及姓名,类方法有设置学生的各科成绩,获取学生的各科目成绩以及显示各科成绩。
cpp
class Student
{
public:
Student(string name_);
Student(string name_, unsigned short yw, unsigned short sx, unsigned short yy);
~Student();
void scoreSet(int yuwen_score_, int shuxue_score_, int yingyu_score_);
unsigned short get_yuwen_score() { return yuwen_score; }
unsigned short get_shuxue_score() { return shuxue_score; }
unsigned short get_yingyu_score() { return yingyu_score; }
void show_score()
{
cout << "语文分数:" << yuwen_score << endl;
cout << "数学分数:" << shuxue_score << endl;
cout << "英语分数:" << yingyu_score << endl;
}
private:
std::string name;
unsigned short yuwen_score;
unsigned short shuxue_score;
unsigned short yingyu_score;
};
类方法定义如下:
cpp
Student::Student(string name_)
{
name = name_;
yuwen_score = 0;
shuxue_score = 0;
yingyu_score = 0;
}
Student::Student(string name_, unsigned short yw, unsigned short sx, unsigned short yy)
{
name = name_;
yuwen_score = yw;
shuxue_score = sx;
yingyu_score = yy;
}
void Student::scoreSet(int yuwen_score_, int shuxue_score_, int yingyu_score_)
{
yuwen_score = yuwen_score_;
shuxue_score = shuxue_score_;
yingyu_score = yingyu_score_;
}
现在从Student类派生出两个类,一个为文科学生的类CulturalStudent,一个为理科学生的类ScienceStudent。
cpp
class CulturalStudent:public Student
{
...
};
class ScienceStudent:public Student
{
...
};
冒号指出,CulturalStudent类和ScienceStudent类的基类是Student类,public表明继承Student类的方式是公有继承,这种派生方式称为公有派生。公有派生的基类的公有成员将成为派生类的公有成员,基类的私有部分也将成为派生类的一部分,但只能通过基类的公有和保护方法访问。
通过公有方式派生出的派生类,其对象具有以下特征:
- 派生类对象存储了基类的数据成员;
- 派生类对象可以使用基类的方法(派生类继承了基类的接口);
因此,CulturalStudent类和ScienceStudent类的对象存储了学生的姓名和语数外三科的成绩,还可以使用Student类中的show_score(),scoreSet()等方法。派生类除了继承基类的方法,还需要添加自己的构造函数、方法以及数据成员。例如,文科学生CulturalStudent类需要添加历史和政治科目的成绩,ScienceStudent类需要添加物理和化学科目的成绩。
CulturalStudent类的声明如下:
cpp
class CulturalStudent:public Student
{
private:
unsigned short lishi_score;
unsigned short zhengzhi_score;
public:
CulturalStudent(string name_, unsigned short yw, unsigned short sx, unsigned short yy,
unsigned short ls, unsigned short zz);
CulturalStudent(const Student& st, unsigned short ls, unsigned short zz);
unsigned short get_lishi_score() { return lishi_score; }
unsigned short get_zhengzhi_score() { return zhengzhi_score; }
};
CulturalStudent类的构造函数定义如下:
cpp
CulturalStudent::CulturalStudent(string name_, unsigned short yw, unsigned short sx, unsigned short yy,
unsigned short ls, unsigned short zz):Student(name_,yw,sx,yy)
{
lishi_score = ls;
zhengzhi_score = zz;
}
CulturalStudent::CulturalStudent(const Student& st, unsigned short ls, unsigned short zz):Student(st)
{
lishi_score = ls;
zhengzhi_score = zz;
}
CulturalStudent类构造函数说明:创建派生类对象时,程序首先创建基类对象。从概念上来说,基类对象应当在程序进入派生类构造函数之前被创建,因此,需要使用成员初始化列表句法(《成员初始化列表句法》)来完成这项工作。
派生类不能直接访问基类的私有成员,需要通过基类方法进行访问。 即CulturalStudent类的构造函数不能直接设置Student类的name,yuwen_score,shuxue_score,yingyu_score成员,需要使用Student类的公有方法来访问Student类的私有成员。此例中CulturalStudent类构造函数使用了Student类的构造函数对name,yuwen_score,shuxue_score,yingyu_score成员进行初始化。Student(name_,yw,sx,yy)、Student(st)都是成员初始化列表,**是可执行的代码,**分别调用了Student(string name_, unsigned short yw, unsigned short sx, unsigned short yy)构造函数和默认的复制构造函数。
如果程序中声明了一个派生类对象,如下:
cpp
CulturalStudent st1("xiaoming", 82, 82, 84, 90, 91);
对象st1的创建过程:CulturalStudent类构造函数将把实参"xiaoming",82,82,84分别赋给形参name_,yw,sx,yy,然后将这些参数作为实参传递给基类Student的构造函数,生成一个嵌套的Student类对象,并将这些数据"xiaoming",82,82,84存储在该对象中。最后,程序进入CulturalStudent类构造函数体,将90,91赋给派生类的私有成员lishi_score,zhengzhi_score,完成st1对象的创建。

如果派生类构造函数定义中省略了成员初始化列表,结果将如何呢。
cpp
CulturalStudent::CulturalStudent(string name_, unsigned short yw, unsigned short sx, unsigned short yy, unsigned short ls, unsigned short zz)
{
lishi_score = ls;
zhengzhi_score = zz;
}
由于基类对象必须先被创建,如果不调用基类构造函数,程序将使用默认的基类构造函数,因此上述定义与下面的构造函数定义等效:
cpp
CulturalStudent::CulturalStudent(string name_, unsigned short yw, unsigned short sx, unsigned short yy, unsigned short ls, unsigned short zz):Student()
{
lishi_score = ls;
zhengzhi_score = zz;
}
但由于Student类没有定义默认的构造函数,所以上面的定义无法通过编译。
如果派生类构造函数定义中省略了成员初始化列表,则将调用基类的默认构造函数。
第二个派生类构造函数中使用了复制构造函数创建基类对象。
cpp
CulturalStudent::CulturalStudent(const Student& st, unsigned short ls, unsigned short zz):Student(st)
{
lishi_score = ls;
zhengzhi_score = zz;
}
通过将CulturalStudent构造函数中的st参数传递给基类的复制构造函数,生成基类对象。
派生类构造函数总结:
- 基类对象首先被创建;
- 派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数;
- 派生类的构造函数应初始化派生类中新增的数据成员;
另外,释放对象的顺序与创建对象的顺序相反,首先执行派生类的析构函数,然后自动调用基类的析构函数。
创建派生类对象时,基类构造函数首先被调用,然后再调用派生类构造函数。基类构造函数负责初始化继承的数据成员,派生类构造函数用于初始化新增的数据成员。派生类构造函数总是调用一个基类构造函数,可以使用初始化列表句法指明要使用的基类构造函数,否则将使用默认的基类构造函数。
当派生类对象过期时,程序首先调用派生类析构函数,然后再调用基类的析构函数。