C++之父力作学习笔记6——派生类
现在来考虑做一个程序,处理某公司所雇佣人员的问题。这个程序可能包含如下一种数据结构: struct Employee
{
string first_name,family_name;
char middle_initial;
Date hiring_date;
short department;
//
};下一步我们可能需要去定义经理: struct Manager
{
Employee emp;
list<Employee*> group; //所管理的人员
short level;
//
};一个经理同时也是一个雇员,所以在Manager对象的emp成员存储着Employee数据。很明显这样的设计是糟糕的,大家都会想到派生:
struct Manager:public Employee
{
list<Employee*> group;
short level;
};这个Manager是由Employee派生的,反过来就是说,Employee是Manager的一个基类。类Manager包含类Employee得所有成员,再加上它自己的一些成员。
按照这种方式从Employee派生出Manager,就使Manager成为Employee的一个子类型,使Manager可以用在能够接受Employee的任何地方。因为Manager是Employee,所以Manager*就可以当做Employee*使用。然而,因为Employee不一定是Manager,所以Employee*就不能当做Manager*用。总而言之类Derived有一个公用基类Base,那么就可以用Derived*给Base*类型的变量赋值,不需要显示的类型转换。而相反的方向,从Base*到Derived*则必须显示转换。例如: void g(Manager mm,Employee ee)
{
Employee* pe = &mm; //可以:Manager都是Employee
Manager* pm = ⅇ //错误:Employee不一定是Manager
pm->level = 2; //灾难:ee没有level
pm = static_cast<Manager*>(pe);//蛮力:这个可以,因为pe指向的是Manager mm
pm->level = 2; //没问题
}
派生类的成员可以使用其基类的公用的和保护的成员,但是,派生类不能使用基类的私有成员。对于派生类的成年公园而言,保护成员就像是公用成员;但对于其他函数它们则像是私用成员。
下面说说派生类的构造函数和析构函数
有些派生类需要构造函数。如果某个基类中有构造函数,那么就必须调用这些构造函数中的某一个。默认构造函数可以被隐含的调用,但是,如果一个基类的所有构造函数都有参数,那么就必须显示的调用其中的某一个。基类构造函数的参数应在派生类构造函数的定义中有明确描述。在这方面,基类的行为恰恰就像是派生类的一个成员。例如: Employee::Employee(const string& n,int d):family(n),department(d)
{
//
}
Manager::Manager(const string& n,int d,int lvl):Employee(n,d),/**//*初始化基类*/level(lvl)/**//*初始化成员*/
{
//
}派生类的构造函数只能描述它自己的成员和自己的直接基类的初始式,它不能直接去初始化基类的成员,例如:
Manager::Manager(const string& n,int d,int lvl):family_name(n).department(d),level(lvl)//错误:在Manager里没有family_name和department的声明
{
//
} 类对象的构造是自下而上进行的:首先是基类,而后是成员,再后才是派生类本身。类对象的销毁则正好以相反的顺序进行:首先是派生类本身,而后是成员,再后才是基类。
对于给定的一个类型为Base*的指针,被指的对象到底属于哪个派生类型呢?这个问题有四种基本的解决方案:
保证被指的只能是唯一类型的对象
在基类里安排一个类型域,供函数检查
使用dynamic_cast
使用虚函数
从Employee的函数中取得“正确的”行为i,而又不依赖于实际使用的到底是哪一种Employee,这就是所谓的多态性。一个带有虚函数的类型被称为是一个多态类型。要在C++里取得多态行为,被调用的函数就必须是虚函数,而对象则必须是通过指针或者引用去操作的。如果直接操作一个对象(而不是通过指针或引用),它的确切类型就已经为编译器所知,因此也就不需要运行时的多态性了。
那么一个虚函数声明为纯虚函数,则这个虚函数所在的类为抽象类。用=0作为初始式就使虚函数成为“纯虚的”。注意:不能创建抽象类的对象。抽象类只能做界面,作为其他类的基类。还有一点也要指的注意,一个未在派生类里定义的纯虚函数仍旧还是一个纯虚函数,这种情况也将使该派生类仍为一个抽象类。例如:
class Shape
{
public:
virtual void draw()=0;
virtual bool isClose()=0;
//
};
class Circel:public Shape
{
public:
bool isClose(){return true;}//覆盖Shap::isClose
//draw尚未覆盖
};
Circel a;//错误:声明的是抽象类Circel对象
抽象类的最重要用途就使提供一个界面,而又不是暴露任何实现的细节。
补充:软件开发 , C++ ,