派生类
1. 继承、派生与多态
继承与 多态是面向对象程序设计中 最重要的两个概念。面向对象程序设计允许从已有类派生出新的类,称为继承(inheritance). 继承是面向对象程序设计中最重要的特征,也是支持代码重用(software reusability)的重要机制。 通过继承,可以定义新类作为现有类的扩展。继承(inheritance):一个新类从已有类那里获得其 已有特性,称为继承。派生(derivation):在已有类的基础上 新增自己的特性而产生新类的过程称为派生。为什么需要继承和派生?如果能在已有的资源基础上设计新类,则能加快编程速度;为什么要继承?新的问题与已解决过的问题有 相似性。为什么要派生改造?新的问题与已解决过的问题又 有差异。多态(polymorphism,源于希腊语,含义为“许多形式”).即在 运行时确定调用哪个函数的能力。
基类与派生类:若一个类C2从另一个类C1扩展而来,则称C2为 派生类(derived class),C1为 基类(base class)。基类也称为父类(parent class), 派生类也称为扩展类(extended class)或子类(child class)。一个派生类继承了其基类的 所有数据成员和成员函数(除构造函数和析构函数外),还可以 增加新的数据成员和函数,从而使派生类更具特殊化。用 指向基类的箭头表示两个类之间的继承关系。如下图:
2. 派生类的定义
class 派生类名:继承方式 基类名{派生类新增成员声明;}
多继承的定义格式:
class 派生类名:继承方式 基类名1,继承方式 基类名2…{派生类新增成员声明;}
例如:
- class Cylinder : public Circle
- {
- protected:
- double h;
- public:
- double getVolume();
- double getH();
- void setH(double);
- double getArea();
- };
其中:
符号“ :”表示继承。例中 Circle 类为 基类,Cylinder类为 派生类。继承方式指定了派生类从基类继承来的成员的访问权限。例中Cylinder为Circle的公有派生类。具体的继承规则下文将会提到。public:公有继承protected:保护继承private:私有继承,若省略则默认为私有继承。派生类成员包括从基类 继承来的所有成员,以及 新增加的成员。例中增加了高度h,求体积函数等。
2.2 保护成员protected
private,protected,public称为 可见性关键字(visibility keyword)或 可访问性关键字(accessibility keyword)。使用protected修饰的成员称为保护成员。 类中的保护成员只能被该类及其 派生类或 友元访问。通常在存在继承关系的类中,基类中的数据成员往往会声明为protected,这样,在其派生类中可直接访问基类的保护成员。可访问性在三种成员中是递增的关系:private→protected→public单继承,例:
class A:public B{…};
则A是派生类,B是A的基类,A从B公有派生。多继承,例:
class A:public B, protected C{…};
则A是派生类,B和C是A的基类,A从B公有派生,从C保护派生。举例如下,Cylinder作为Circle的派生类:
- //circle.h
- #ifndef CIRCLE_H
- #define CIRCLE_H
- const double pi=3.14159;//extern怎么用,不能加extern不然报错
- class Circle
- {
- public:
- Circle(double r=1);
- double getArea();
- double getPerimeter();
- double getRadius();
- void setRadius(double r);
- protected:
- double radius;//若radius在Circle类中为private,
- //则在Cylinder类中不可访问。
- private:
- };
- #endif
- //cylinder.h
- #ifndef CYLINDER_H
- #define CYLINDER_H
- #include "circle.h"//必须包含circle.h类声明
- class Cylinder:public Circle
- {
- public:
- double getSurface();
- double getVolume();
- double getHeight();
- void setHeight(double r);
- protected:
- private:
- double height;
- };
- #endif
- //circle.cpp
- #include "circle.h"
- Circle::Circle(double r)//不能带默认参数
- {
- radius=r;
- }
- double Circle::getArea()
- {
- return radius*radius*pi;
- }
- double Circle::getPerimeter()
- {
- return radius*2*pi;
- }
- double Circle::getRadius()
- {
- return radius;
- }
- void Circle::setRadius(double r)
- {
- radius=r;
- }
- //cylinder.cpp
- #include "cylinder.h"
- double Cylinder::getSurface()
- {
- return pi*radius*radius*2+2*pi*radius*height;
- }
- double Cylinder::getVolume()
- {
- return pi*radius*radius*height;
- }
- double Cylinder::getHeight()
- {
- return height;
- }
- void Cylinder::setHeight(double r)
- {
- height=r;
- }
- //test.cpp
- #include "circle.h"
- #include "cylinder.h"
- #include <iostream>
- using namespace std;
- void main()
- {
- Circle a(2);
- cout<<a.getRadius()<<endl;
- Cylinder b;
- b.setRadius(1);
- b.setHeight(2);
- cout<<b.getHeight()<<b.getRadius()<<endl;
- }
可以看出派生类还是十分好用的。在进行派生类声明时仅需加:可见性关键字 基类名即可。
2.3 派生类成员的构成
派生类中的成员包括:从基类继承的成员:除构造函数和析构函数外,全部接收;通过继承方式改变基类成员在派生类中的访问属性。新增加的成员:定义派生类自己的构造函数和析构函数同名覆盖(override): 派生类中重新定义与基类成员同名的新成员(函数原型说明相同),则可屏蔽基类的同名成员。可见,继承和派生的目的在于 复用基类的某些属性,并对基类进行 裁剪,以定制出适合于某种需要的新类。
2.4 派生类的继承方式
派生类对基类不同的继承方式决定了基类成员在派生类中的访问属性。派生类的继承方式有三种:public:公有继承protected:保护继承private:私有继承继承规则如下表:
注意:无论哪种继承方式,在派生类中都是不能访问基类中的私有成员的,私有成员只能被本类的成员函数和友元访问。
2.4.1 公有继承
当派生类的继承方式为公有继承时,基类各成员在派生类中的访问权限:公有成员:成为派生类的公有成员保护成员:成为派生类的保护成员私有成员:为基类私有,无论是在派生类中,还是在类外都不可访问。在派生类只能访问基类中的public和protected成员, 不能访问private成员。派生类的对象(在派生类外)只能访问基类中的public成员和派生类自己的public成员。公有继承 基本保持了基类的访问属性,因此使用较多。代码示例如下:
- //inheritance.cpp
- #include <iostream>
- using namespace std;
- class CA
- {
- private:
- int x;
- protected:
- int y;
- public:
- void setX(int a)
- { x=a; }
- void setY(int b)
- { y=b; }
- int getX()
- { return x; }
- int getY()
- { return y; }
- };
- class CB: public CA //公有继承
- {
- public:
- int getSum()
- {
- return y+getX();//不能return y+x;
- }
- };
- void main()
- {
- CB b;
- b.setX (2);
- b.setY(3); //b.y=3;error
- cout<<"x="<<b.getX()<<"\ty="<<b.getY()<<endl; cout<<"x+y="<<b.getSum ()<<endl;
- }
基类和派生类中的公有成员都可以直接通过派生类的对象名来访问。类的保护成员只能被本类的成员函数或它的派生类的成员函数访问,在类外不能直接访问。
2.4.2 私有继承
代码示例:当类的派生方式为私有继承时:基类的所有public和protected的成员在派生类中的访问权限变为private。基类的私有成员仍为基类私有。基类的公有成员和保护成员被派生类继承过来以后,成为了派生类的 私有成员。在派生类中只能访问基类中的public/protected成员,而 不能访问private成员。派生类的对象 不能访问基类中的任何成员。(对象不能访问函数的private成员)
- //inheritance.cpp
- #include<iostream>
- using namespace std;<pre code_snippet_id="211569" snippet_file_name="blog_20140301_8_5409242" name="code" class="cpp"><div><div>class CA
- </div><div>{
- </div><div>private:
- </div><div> int x;
- </div><div>protected:
- </div><div> int y;
- </div><div>public:
- </div><div> void setX(int a)
- </div><div> { x=a; }
- </div><div> void setY(int b)
- </div><div> { y=b; }
- </div><div> int getX()
- </div><div> { return x; }
- </div><div> int getY()
- </div><div> { return y; }
- </div><div>};
- </div></div><div><div>class CB: private CA
- </div><div>{
- </div><div>public:
- </div><div> void setBx(int a)
- </div><div> { setX(a); }
- </div><div> void setBy(int b)
- </div><div> { y=b; }
- </div><div> int getBx()
- </div><div> { return getX(); }
- </div><div> int getBy()
- </div><div> { return y; }
- </div><div> int getSum()
- </div><div> { return y+getX(); }
- </div><div>};
- </div></div><div><div>void main()
- </div><div>{
- </div><div> CB b;
- </div><div> b.setBx (2); //b.setX(2); error
- </div><div> b.setBy(3);
- </div><div> cout<<"x="<<b.getBx()<<"\ty="<<b.getBy()<<endl;
- </div><div> cout<<"x+y="<<b.getSum ()<<endl;
- </div><div>}</div></div></pre>
- <pre></pre>
- <div>
- <div>私有派生:基类中公有/保护成员在派生类中变为私有成员。</div>
- </div>
- <pre></pre>
2.4.3 保护继承
当类的派生方式为保护继承时:基类的所有公有public和保护protected的成员在派生类中的访问权限 变为protected。基类的私有成员仍为基类私有。基类中的公有和保护成员被派生类继承过来,作为派生类中的保护成员。在派生类中只能访问基类中的public/protected成员,而不能访问private成员。派生类的对象不能访问基类中的任何成员。
保护继承和私有继承的区别:
用私有继承方式派生出来的类,从基类中继承的公有和保护成员被当作这个类的私有成员,如果这个类去派生其他类,它派生出来的类的成员函数将 无法访问这些私有成员;采用保护方式派生出来的类从基类中继承的公有和保护成员被当作这个类的保护成员,如果用这个类去派生其他类,它的派生类的成员函数将 可以直接访问这些成员。
3. 小结
本节讨论了派生类的定义与继承规则。派生类在声明时需在头部指明它的继承方式与基类。派生类在继承时有三种方式,分别是公有继承、私有继承和保护继承。可做如下练习题,来讨论派生类的继承问题。
练习:
类A、B、C的声明如下:
- class A
- {
- private:
- int i1;
- protected:
- int j1;
- public:
- A()
- {
- i1=10;
- j1=11;
- }
- void f1();
- };
- class B:public A
- {
- private:
- int i2;
- protected:
- int j2;
- public:
- B()
- {
- i2=20;
- j2=21;
- }
- void f2();
- };
- class C:public B
- {
- private:
- int i3;
- protected:
- int j3;
- public:
- C()
- {
- i3=30;
- j3=31;
- }
- void f3();
- };
问:
答:1). B中成员函数能否访问A中的f1(),i1,j1?2). B的对象b能否访问A中的f1(),i1,j1?3.) C中f3()能否访问B中的f2(),j2以及A中的f1(),i1,j1?4.) C的对象c能否访问B中的成员f2(),i2和j2?能否访问A中的f1(),i1,j1?继承关系若全为:private 或 protected ,情况如何?
1. 公有继承public时:基类的public和protected成员被派生类继承,并且其属性不变;Private成员不继承。
1)能访问f1(),j1,不能访问i1.
2)能访问f1(),不能访问i1,j1
3)能访问f2(),j2(),f1(),j1,不能访问i1.
4)能访问f2(),f1(),不能访问i2,j2,i1,j1
2. 私有继承private时: 基类的所有public和protected的成员在派生类中的访问权限变为private; 基类的私有成员仍为基类私有,不继承。
1)能访问f1(),j1,不能访问i1.
2)不 能访问f1(),不能访问i1,j1
3)能访问f2(),j2(),f1(),不能访问i1,j1.
4)不 能访问f2(),f1(),不能访问i2,j2,i1,j1
3. 保护继承protected时: 基类的所有公有public和保护protected的成员在派生类中的访问权限变为protected; 基类的私有成员仍为基类私有。公有成员可以继续派生了。
1)能访问f1(),j1,不能访问i1.
2)不 能访问f1(),不能访问i1,j1
3)能访问f2(),j2(),f1(),不能访问i1,j1.
4)不 能访问f2(),f1(),不能访问i2,j2,i1,j1