06、面向对象程序实现-继承与多态性(I)-2010

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

c的静态类型
c的动态类型
9-11

对继承的狙击
有时候并不希望由客户程序自定义的子类代替某些类! Java里有很好的机制将一个类定义为final的或者将一个成员定义 为final的。 C++里如何实现禁止类被派生?

解决方案
1 构造函数声明为私有的。如果用户从该类派生一个类,那么在编译阶段就会得到 一个不能访问私有成员函数的错误信息。 2 创建伪造的构造函数:静态,返回的是对象指针 3 注意:用户在使用完该类对象后需要调用delete,释放资源。也可使用智能指针。
Lecture Notes on
Object-Oriented Technology
(Programming & Design)
(Fall 2010, Bachelor of Computer Science)
Duan shihong
duansh@ies.ustb.edu.cn Office: Room 1203A ,Information Building
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
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); } 如果把类的构造函数声明为私有的,那么 我们就无法构造这个类的对象,
9-48
派生类的构造函数
派生类构造函数的一般形式: 派生类名::派生类名(基类所需的形参,本类成员所需的形参): 基类1(基类参数表1), „,基类n(基类参数表n), 对象成员1(对象参数表1), „,对象成员m(对象参数表m) { 本类基本类型数据成员初始化; } 1. 调用基类构造函数,调用顺序按照它们被继承时声明的顺序 (从左向右)。 2.调用成员对象的构造函数,调用顺序按照它们在类中声明的 顺序。 3. 派生类的构造函数体中的内容。
inaccessible inaccessible inaccessible
9-23
构造方法未被继承

子类继承了什么
子类继承了父类和所有成员,包括方法和变量(域)。
构造方法并不是一个类的成员,所以没有被继承。

在C++中不能被继承的部分
1、构造函数;
2、析构函数;
3、用户定义的操作符;
4、用户定义的赋值符; 5、友元关系。
9-15
重定义的继承模式(is-a)
Figure
draw()
Triangle
Rectangle
Circle
draw()
draw()
draw()
9-16
注意圆与点之间 关系不是has-a关系
扩充的继承模式(is-like-a)
Location x, y: int;
将圆看作一种 带有半径的点
Point
incompatible types
9-10

wenku.baidu.com
静态类型 vs 动态类型
静态类型:声明obj时的类型(在编译时确定的类型)。 动态类型:在运行时某一时刻与obj相关联的对象的类型。 由于基本类型的静态与动态类型没有区别,故只需讨论引用类型。 动态类型必须是静态类型的子类型!
Cleanser *c = new Detergent();
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();
ColoredBox cb; void main() { cb.SetWidth(5); cb.SetHeight(5); cb.SetColor(6); )
9-6
class Cleanser { private: string s; protected: void append(string a) { s += a; } public: Cleanser() {s = “Cleanser”;} void dilute() { append(" dilute()"); } void apply() { append(" apply()"); } void scrub() { append(" scrub()"); } void print() { cout<<s<<endl; } static void test() { Cleanser x; x.dilute(); x.apply(); x.scrub(); 嵌入式测试 x.print(); } } class Detergent: public Cleanser { public: void scrub() { // 重定义父类的方法 append(" Detergent.scrub()"); Cleanser::scrub(); // 调用父类版本的方法 } void foam() { // 子类引入的新方法 append(" foam()"); } }; void main( ) { Detergent x; x.dilute(); x.apply(); x.scrub(); x.foam(); x.print(); Cleanser::test( ); }
9-20
继承概念1 - 继承方式
三种继承方式:
公有继承(public), 私有继承(private), 保护继承(protected)
不同继承方式的影响主要体现在:
•派生类成员对从基类继承的成员的访问控制。 •派生类对象对从基类继承成员的访问控制。
9-21
继承方式-继承的访问控制
不同的继承方式使得派生类从基类继承 的成员具有不同的访问控制权限,以实现 数据的安全性和共享性控制。 派生类成员(继承的成员+自增加的成 员)的访问权限:
Tommy is a cat. 泛化(generalization):描述类型与类型之间的关系(子类型关系)。
Cats are animals.

注意它们的区别
泛化关系具有传递性;而分类关系不具有传递性。 继承所指的is-a关系是指泛化,而不是分类。 有学者提出新名词:用kind-of关系代替is-a关系。 The cat ia a kind of animals.
1)inaccessible(不可访问) 2)public 3)private 4)protected
9-22
存取方式 继承类型 public protected private
public
protected
private
public protected private
protected protected private
9-52
多继承的经典例子(C++语言的iostream.h)
istream
ostream
iostream
9-60
派生类的定义格式
{ public: //派生类公有成员… private: //派生类私有成员… };
有多个基类
class 派生类名:继承方式 基类名1, … 继承方式 基类名n
9-1
第八讲 继承与多态性(I)
• •
软件复用及其途径 继承:泛化的实现 ★★★ 继承中的对象初始化与收尾 ★★ 多继承与重复继承 继承的实例:纸牌游戏

• •

主题讨论:存储模型,与继承相关的两个设计原则★★★
9-2

两种is-a关系
分类(classification):描述实例与类型之间的关系。
9-3
继承机制

继承是is-a关系在程序中的实现
is-a关系:在问题空间中描述概念与概念之间的关系。 利用现有概念来定义一个新的概念。 继承关系:在解空间中描述类与类之间的关系。 利用现有的类来定义一个新的类。

软件复用的思想
面向对象设计的一个重要指导原则是:不要每次都从头开始定义 一个新的类,而是将这个新的类作为一个或若干个现有类的泛化
或特化。
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)
9-12
class ClxNotBase { public: ~ClxNotBase();
构造函数 私有,禁 止派生
private: ClxNotBase(); ClxNotBase(const ClxNotBase& rhs); };
如果把类的构造函数声明为私有的,那么 我们就无法构造这个类的对象,
9-7

继承为访问控制增多了一种角色
最早的访问控制角色:
自己 他人 新的访问控制角色:
自己 子孙 他人

为此新增了一种访问控制方式
原有的访问控制方式: private public 新的访问控制方式: private protected public
9-8

子类型
内涵越大、外延越小(或曰:内涵越小、外延越大): 子类的内涵只会比父类大。(只能增加与修改,无法删除) 故:子类的实例集 其父类的实例集。 故:在需要父类类型之处,可代以其子类的对象实例。 私有财产不会继承? 同样保存在子类的对象实例中。 每一个detergent的实例都含有父类的私有属性s。 只是在detergent的方法体中不能访问而已。 由此可见,父类创造的财富应由父类在管理。 detergent实例的s也应由cleanser的构造方法初始化。
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-5
继承
class ColoredBox:public Box class Box { { public: public: int width, height; int color; void SetWidth(int); void SetColor(int); void SetHeight(int); }; };
相关文档
最新文档