当前位置:
文档之家› 06、面向对象程序实现-继承与多态性(I)-2010
06、面向对象程序实现-继承与多态性(I)-2010
9-15
重定义的继承模式(is-a)
Figure
draw()
Triangle
Rectangle
Circle
draw()
draw()
draw()
9-16
注意圆与点之间 关系不是has-a关系
扩充的继承模式(is-like-a)
Location x, y: int;
将圆看作一种 带有半径的点
Point
1)inaccessible(不可访问) 2)public 3)private 4)protected
9-22
存取方式 继承类型 public protectedted
private
public protected private
protected protected private
9-52
多继承的经典例子(C++语言的iostream.h)
istream
ostream
iostream
9-60
派生类的定义格式
{ public: //派生类公有成员… private: //派生类私有成员… };
有多个基类
class 派生类名:继承方式 基类名1, … 继承方式 基类名n
visible: boolean;
Circle
radius: double;
9-17
例: 定义基类Pen
class Pen { public: enum ink {off,on}; void set_status (ink); void set_location (int, int); private: int x; int y; int status; //状态 };
9-1
第八讲 继承与多态性(I)
• •
软件复用及其途径 继承:泛化的实现 ★★★ 继承中的对象初始化与收尾 ★★ 多继承与重复继承 继承的实例:纸牌游戏
•
• •
•
主题讨论:存储模型,与继承相关的两个设计原则★★★
9-2
•
两种is-a关系
分类(classification):描述实例与类型之间的关系。
9-20
继承概念1 - 继承方式
三种继承方式:
公有继承(public), 私有继承(private), 保护继承(protected)
不同继承方式的影响主要体现在:
•派生类成员对从基类继承的成员的访问控制。 •派生类对象对从基类继承成员的访问控制。
9-21
继承方式-继承的访问控制
不同的继承方式使得派生类从基类继承 的成员具有不同的访问控制权限,以实现 数据的安全性和共享性控制。 派生类成员(继承的成员+自增加的成 员)的访问权限:
9-5
继承
class ColoredBox:public Box class Box { { public: public: int width, height; int color; void SetWidth(int); void SetColor(int); void SetHeight(int); }; };
9-3
继承机制
•
继承是is-a关系在程序中的实现
is-a关系:在问题空间中描述概念与概念之间的关系。 利用现有概念来定义一个新的概念。 继承关系:在解空间中描述类与类之间的关系。 利用现有的类来定义一个新的类。
•
软件复用的思想
面向对象设计的一个重要指导原则是:不要每次都从头开始定义 一个新的类,而是将这个新的类作为一个或若干个现有类的泛化
inaccessible inaccessible inaccessible
9-23
构造方法未被继承
•
子类继承了什么
子类继承了父类和所有成员,包括方法和变量(域)。
构造方法并不是一个类的成员,所以没有被继承。
•
在C++中不能被继承的部分
1、构造函数;
2、析构函数;
3、用户定义的操作符;
4、用户定义的赋值符; 5、友元关系。
9-14
创建伪造 的构造函 数
继承的模式
•
两种不同的继承模式
① 重定义(overriding):仅重定义父类的操作而不引入新特征。 是比扩充更为重要、更加常见的继承模式。 子类型的接口与父类型完全相同,两者是完全相同的类型。
② 扩充(extending):引入父类所没有的新特征。
子类型与父类型有区别:更加丰富的内容,是父类型的特例。 Java语言的保留字extends表明了这种继承。 ③ 实际应用通常是上述两种方式的结合。
9-9
•
类型兼容
子对象(subobject):每一个子类的对象实例中都有一个父类的子对象。
子对象的类型是父类类型。
向上转换(upcasting):又称widening reference conversion。 S T,S是T的子类 不需要运行时检测,允许隐式转换。
Cleanser *c = new Detergent(); C->scrub(); //! Detergent *d = new Cleanser(); //! D->foam();
9-13
class ClxNotBase {public: ~ClxNotBase(); static ClxNotBase * NewlxNotBase(); static ClxNotBase * NewlxNotBase(const ClxNotB ase& rhs); private: ClxNotBase(); ClxNotBase(const ClxNotBase& rhs); }; ClxNotBase * ClxNotBase::NewlxNotBase() { // 调用真正的构造函数 return new ClxNotBase(); } ClxNotBase * ClxNotBase::NewlxNotBase(const Clx NotBase& rhs) { // 调用真正的拷贝构造函数 return new ClxNotBase(rhs); } 如果把类的构造函数声明为私有的,那么 我们就无法构造这个类的对象,
Tommy is a cat. 泛化(generalization):描述类型与类型之间的关系(子类型关系)。
Cats are animals.
•
注意它们的区别
泛化关系具有传递性;而分类关系不具有传递性。 继承所指的is-a关系是指泛化,而不是分类。 有学者提出新名词:用kind-of关系代替is-a关系。 The cat ia a kind of animals.
9-19
x y status set_status set_location
定义派生类(彩色钢笔)
class CPen: public Pen { public: void set_color(int); private: int color; };
x y status color
set_status set_location set_color
Lecture Notes on
Object-Oriented Technology
(Programming & Design)
(Fall 2010, Bachelor of Computer Science)
Duan shihong
duansh@ Office: Room 1203A ,Information Building
9-48
派生类的构造函数
派生类构造函数的一般形式: 派生类名::派生类名(基类所需的形参,本类成员所需的形参): 基类1(基类参数表1), „,基类n(基类参数表n), 对象成员1(对象参数表1), „,对象成员m(对象参数表m) { 本类基本类型数据成员初始化; } 1. 调用基类构造函数,调用顺序按照它们被继承时声明的顺序 (从左向右)。 2.调用成员对象的构造函数,调用顺序按照它们在类中声明的 顺序。 3. 派生类的构造函数体中的内容。
9-12
class ClxNotBase { public: ~ClxNotBase();
构造函数 私有,禁 止派生
private: ClxNotBase(); ClxNotBase(const ClxNotBase& rhs); };
如果把类的构造函数声明为私有的,那么 我们就无法构造这个类的对象,
c的静态类型
c的动态类型
9-11
•
对继承的狙击
有时候并不希望由客户程序自定义的子类代替某些类! Java里有很好的机制将一个类定义为final的或者将一个成员定义 为final的。 C++里如何实现禁止类被派生?
•
解决方案
1 构造函数声明为私有的。如果用户从该类派生一个类,那么在编译阶段就会得到 一个不能访问私有成员函数的错误信息。 2 创建伪造的构造函数:静态,返回的是对象指针 3 注意:用户在使用完该类对象后需要调用delete,释放资源。也可使用智能指针。
或特化。
9-4
class X{ int i; public: X( ) { i = 0;} void f(); }; class Y{ int j; public: X x; Y( ) { i = 0;} … };
X的对象
i
Y的对象
i j
子对象x
main() { Y y; y.x.f(); }
(composition)
ColoredBox cb; void main() { cb.SetWidth(5); cb.SetHeight(5); cb.SetColor(6); )
9-6