一、类的继承与派生
引:
类的派生机制的好处:在于代码的重用性和可扩充性。
派生新类的过程的三个步骤:①吸收已有类的成员;②调整已有类成员;③添加新的成员。
1)基本概念:
类的继承是新的类从已有类那里得到的已有特性。即,从已有类产生新类的过程就是类的派生
如下图:
上下层之间的关系就可以看作基类与派生类之间的关系
基类(父类):原有的类
派生类(子类):产生的新类
类的层次结构:派生类同样也可以作为基类派生新的类
2)派生类的定义
class 派生类名:继承方式 基类名1,继承方式 基类名2,...,继承方式 基类名n{ 派生类成员声明; };
例如:
class Derived:public Base1,private Base2//基类名Base1 Base2;派生类名Derived{public: Derived(); ~Dderived(); };
注意:
①一个派生类可以同时有多个基类,称为多继承
②一个派生类只有一个直接基类的情况,称为单继承
③直接基类:在类族中,直接参与派生出某类的基类
④间接基类:基类的基类甚至更高层的基类
3)继承方式
规定如何访问从基类继承的成员,指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限。
关键字有:①public 公有继承 ②private 私有继承 ③protected 保护继承。
派生类成员:指除了从基类继承来的所有成员之外,新增加的数据和函数成员
注:派生类不能直接使用公有继承的基类私有成员,但可以直接使用基类的公有、保护成员。
二、派生类的生成过程
①吸收基类成员
②改造基类成员
如果派生类声明了一个和某基类成员同名的新成员(如果是成员函数,则参数表也要相同,参数表不同的情况属于重载),派生的新成员就隐藏了外层同名成员。
③添加新的成员
三、访问控制----三大继承方式
访问属性的不同:一是派生类中的新增成员访问从基类继承的成员;二是在派生类外部(非类组内成员),通过派生类的对象访问从基类继承的成员。
1、公有继承(public)
当类的继承方式为公有继承时,类的公有成员和保护成员的访问属性在派生类中不变,二基类的私有成员不可直接访问。
例:7-1
#includeusing namespace std;class Point { //基类Point类的定义public: //公有函数成员 void initP(float x=0, float y=0) { this->x = x; this->y = y; } void Move(float xOff, float yOff) { x += xOff; y += yOff; } float GetX() const { return x; } float GetY() const { return y; }private: //私有数据成员 float x, y;};class Rectangle :public Point { //派生类的定义public: void initR(float x, float y, float W, float H) //新增公有函数成员 { initP(x, y); //调用基类公有成员函数 this->W = W; this->H = H; } float GetH() const { return H; } float GetW() const { return W; }private: float W, H; //新增私有数据成员};int main() { Rectangle rect; //定义Rectangle类的对象 rect.initR(2, 3, 20, 10);//设置矩形的数据 rect.Move(3, 2); //移动矩形位置 cout << "The data of rect(x,y,W,H):" << endl; cout << rect.GetX() << "," << rect.GetY() << "," << rect.GetW() << "," << rect.GetH() << endl; //输出矩形的特征参数 return 0;}
程序的运行结果:
理解:
主函数声明了派生类的对象rectangle(对象生成时调用了系统所产生的默认构造函数,函数的功能是什么都不做),通过派生类的对象,访问了派生类的公有成员函数initR,move等,也访问了派生类从基类继承来的公有函数GetX(),GetY()。派生类成员函数只能访问基类的公有成员和保护成员,但是通过派生类的对象只能访问基类的公有成员。
2、私有继承(private)
当类的继承方式为私有继承时,基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生中不可直接访问。
(基类的公有成员和保护成员被继承后作为派生类的私有成员,派生类的其他成员可以直接访问他们,但是在类族外部通过派生的的对象无法直接访问他们。也就是说,无论是派生类的成员还是通过派生类的对象都无法直接访问从基类继承的私有成员)私有继承实际上相当于终止了基类功能的继续派生。
例:将上述例题公有继承改为私有继承----程序报错。
class Rectangle :private Point {public: void initR(float x, float y, float W, float H) { initP(x, y); this->W = W; this->H = H; } float GetH() const { return H; } float GetW() const { return W; }private: float W, H;};
注:
报错提醒即为“派生类的私有继承使得基类中定义的成员函数无法再派生类中访问”。
修改:
class Rectangle :private Point {public: void initR(float x, float y, float W, float H) { initP(x, y); this->W = W; this->H = H; } void Move(float xOff, float yOff) { Point::Move(xOff, yOff); //在派生类中重新声明同名成员 } float GetX() const { return Point::GetX(); } //在派生类中重新声明同名成员 float GetY() const { return Point::GetY(); } //在派生类中重新声明同名成员 float GetH() const { return H; } float GetW() const { return W; }private: float W, H;};
运行结果:
理解:
①继承方式为私有继承,派生类的成员函数及对象无法直接访问基类的私有成员(例如基类的x,y)。派生类的成员仍然可以访问从基类继承过来的公有和保护成员(例如,派生类函数成员initR中直接调用基类的函数initP),但是在类外部通过派生类的对象根本无法直接访问到基类的任何成员,基类原本的外部接口(例如,GetX(),GetY()函数)被派生类封装和隐藏起来。
②为了保证基类的一部分外部接口特征能够在派生类中也存在,就必须在派生类中重新声明同名成员。这里的派生类Rectangle中,重新声明了Move,GetX(),GetY()等函数,利用派生类对基类成员的访问能力,把基类的原有成员函数的功能照搬过来。(在派生类中重新声明的成员函数具有比基类的原有成员函数更小的作用域,因此在调用的时候根据同名隐藏原则,使用派生类的函数)
③主函数与7-1完全相同,但执行过程不同。此时Rectangle类对象rect调用的都是派生类自身的公有成员(以为私有继承,它不可能访问任何一个基类成员)。
3、保护继承(protected)
保护继承中,基类的公有成员函数和保护成员函数都以保护成员的身份出现在派生类中,而基类的私有成员不可直接访问。
(派生类的其他成员可以直接访问从基类继承来的公有和保护成员,但在类的外部通过派生类的对象无法直接访问他们。无论是派生类的成员还是派生类的对象都无法直接访问基类的私有成员。)
例:
class A{ protected: //保护成员数据 int x;};int main(){ A a; a.x=5; //错误!主函数不允许访问A类的保护成员(访问规则与A类的私有成员相同)}
再如:
class A { protected: int x;};class B :public A { //公有派生 public: void function(); }; void B::function() { //再派生类B的成员函数内部是完全可以访问基类的保护成员的 x = 5; }
注意:
如果B是A的派生类,B的成员函数只能通过B的对象访问A中定义的protected成员,而不能通过A的对象访问A的protected成员。
▲私有继承与保护继承比较:
①实际上在直接派生中,所有成员的访问属性是完全相同的。
②如果派生类作为新的基类继续派生时,二者出现区别。
B类以私有继承方式继承A后,B有派生出了C类,那么C类的成员和对象都不能访问间接从A类继承来的成员,
B类以保护继承方式继承A后,B有派生出了C类,A类中的公有和保护成员被C类间接继承后,有可能是保护的或者是私有的(视从B到C类的派生方式不同而不同)
四、总结
①无论哪种继承方式,派生类中新增成员可以访问基类的公有成员和保护成员,无法访问私有成员。
②只有公有继承方式通过派生类的对象可以访问基类中的public成员(且只能访问public成员),保户继承和私有继承在通过派生类的对象访问时不可以访问基类中任何数据成员。
③友元函数在他的函数体中可以通过对象名访问类的私有和保护成员。