ImageVerifierCode 换一换
格式:DOCX , 页数:7 ,大小:19.57KB ,
资源ID:8302551      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/8302551.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(C课件 第9章 继承.docx)为本站会员(b****5)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

C课件 第9章 继承.docx

1、C课件 第9章 继承 C课件 第9章 继承 第九章 面向对象 1-继承 这一章的内容包括: 基类和派生类 派生类的构造函数/析构函数 子类型化和类型适应 多继承 虚基类 一、 基类和派生类 1 概念 通过继承机制, 可以利用已有的数据类型来定义新的数据类型。 所定义的新的数据类型不仅拥有新定义的成员, 而且还同时拥有旧的成员。 我们称已存在的用来派生新类的类为基类, 又称为父类。 由已存在的类派生出的新类称为派生类, 又称为子类。 在 C+语言中, 一个派生类可以从一个基类派生, 也可以从多个基类派生。 从一个基类派生的继承称为单继承; 从多个基类派生的继承称为多继承。 2 语法 单继承的定义

2、格式如下: class 派生类名: 继承方式 基类名 /派生类新定义成员 ; 其中, 派生类名是新定义的一个类的名字, 它是从基类名中派生的, 并且按指定的继承方式派生的。 继承方式常使用如下三种关键字给予表示: public 表示公有基类; private 表示私有基类; protected 表示保护基类; 示例: class MyDate: public Date1 /派生类新定义成员变量或函数 ; /派生类新定义成员变量(或函数) 初始化(或实现) 3 派生类的三种继承方式 公有继承(public)、 私有继承(private)、 保护继承(protected)是三种继承方式。 (1)

3、公有继承(public) 公有继承的特点是基类的 private、 public 和protected 成员作为派生类的成员时, 它们都保持原有的状态。 (2) 私有继承(private) 私有继承的特点是基类的 public 成员和 protected成员, 在派生类都变为私有成员。 基类的原私有成员仍为派生类的私有成员 (3) 保护继承(protected) 保护继承的特点是基类的 public 成员和 protected成员, 在派生类都变为保护成员。 基类的原私有成员仍为派生类的私有成员。 下表列出三种不同的继承方式的基类特性和派生类特性: 继承方式 基类成员 派生类成员 公有继承 p

4、ublic protected private public protected private 私有继承 public protected private 变为 private 变为 private private 保护继承 public protected private 变为 protected protected private 对于单级继承来说, 讨论保护继承与私有继承的区别意义是不大的, 他们的区别只在多级继承的情况中体现。 私有继承在一些特定场合下可用于表示类的组成关系。 保护继承与私有继承在实际编程中是极其少见的。 Q1: C+默认的继承方式? 对于 C+的类(class) ,

5、 默认的继承方式是私有继承(private), 而最常用的继承方式是公有继承(public)。 对于 C+的结构体(struct) 默认的继承方式是公有继承(public)。 4 基类与派生类的关系 任何一个类都可以派生出一个新类, 派生类也可以再派生出新类, 因此, 基类和派生类是相对而言的。 基类与派生类之间的关系可以有如下几种描述, 了 解这些关系有助于指导实际编程工作: ( 1) 派生类是基类的具体化 类的层次通常反映了 客观世界中某种真实的模型。 在这种情况下, 不难看出: 基类是对若干个派生类的抽象,而派生类是基类的具体化。 基类抽取了 它的派生类的公共特征, 而派生类通过增加行为

6、将抽象类变为某种有用的类型。 ( 2) 派生类是基类定义的延续 先定义一个抽象基类, 该基类中有些操作并未实现。 然后定义非抽象的派生类, 实现抽象基类中定义的操作。 例如, 虚函数就属此类情况。 这时, 派生类是抽象的基类的实现, 即可看成是基类定义的延续。 这也是派生类的一种常用方法。 ( 3) 派生类是基类的组合 在多继承时, 一个派生类有多于一个的基类, 这时派生类将是所有基类行为的组合。 比如以前讲过的飞机组成的例子, 用组成关系和多继承都可以, 但最好使用组成关系。 派生类将其本身 与基类区别开来的方法是添加数据成员和成员函数。 因此, 继承的机制将使得在创建新类时,只需说明新类与

7、已有类的区别, 从而大量原有的程序代码都可以复用, 所以有人称类是可复用的软件构件。 在实际编程中, 一定要注意, 继承是软件体系结构方面的问题: ( 1) 派生类最好是基类的一类(is a kind of) , 否则容易出现设计概念错误。 (2) 继承会增加空间复杂度和时间复杂度, 要慎重使用。 继承链不要太长(3-5 层足矣) , 要头轻脚重 , 越是顶层的类越要轻巧-最好是完全抽象的虚类。 (3) 优先使用组成关系(有的书叫聚集、 聚合、 复合) ,而不是继承关系解决问题。 继承把抽象化角色(基类) 和实现化角色(派生类) 的关系绑定, 使得两个层次之间产生了相互依赖和限制, 很难独立地

8、演化, 在软件设计中应避免此类问题。 组成关系没有这个缺点。 (4) 基类要坚固不变 , 否则派生类。 如果基类的变化是必需的, 那应该在派生类和基类之间加一个设计概念上更抽象的第三者 类, 用这个第三者 的类去破坏 原有的继承关系。 想想为什么数据库访问要通过 JDBC 等接口 。 Q2: 实际编程中使用继承应注意哪些问题? 要求对课件讲到的做 1 -2 点补充。 Q3: 你对优先使用组成关系, 而不是继承关系 是怎样理解的? 要有自 已的观点。 二 派生类的构造函数/析构函数 当一个派生类被实例化时, 其基类也被实例化, 实际上一个派生类对象是由其基类对象和派生类对象 组装而成,了 解这一

9、点才能了 解派生类 构造函数/析构函数 的有关特性。 1. 派生类的构造函数 构造函数不能够被继承, 因此, 派生类的构造函数必须通过调用基类的构造函数来初始化派生类对象。 所以, 在定义派生类的构造函数时除了 对自己的数据成员进行初始化外, 还必须负责调用基类构造函数使基类数据成员得以初始化。 如果派生类中还有子对象时(比如将某个类的对象作为派生类的成员变量), 还应包含对子对象初始化的构造函数。 派生类构造函数的一般格式如下: 派生类名(派生类构造函数总参数表):基类构造函数(参数表 1),子对象名(参数表 2) /派生类中数据成员初始化 ; 派生类构造函数的调用顺序如下: 基类的构造函数

10、 子对象类的构造函数(如果有的话) 派生类构造函数 例: class A private: int a; public: A() a=0; /缺省构造函数 A(int i) a=i; /构造函数 A() /类 A 的析构函数 class B : public A private: int b; A aa; /子对象 aa public: B() b=0; /类 B 的缺省构造函数 B(int i, int j, int k); /构造函数 B() /类 B 的析构函数 /类 B 构造函数, 注意顺序 B:B(int i, int j, int k) : A(i) , aa(j) b=k; 派生

11、类构造函数使用中应注意的问题 (1) 派生类构造函数的定义中可以省略对基类构造函数的调用, 其条件是在基类中必须有缺省的构造函数或者根本没有定义构造函数。 (2) 当基类的构造函数使用一个或多个参数时, 则派生类必须定义构造函数, 提供将参数传递给基类构造函数途径。 在有的情况下, 派生类构造函数的函数体可能为空, 仅起到参数传递作用。 2. 派生类的析构函数 当派生类对象被删除时, 派生类的析构函数被执行。 执行顺序与执行构造函数时的顺序正好相反。 三、 子类型化和类型适应 这一小节要掌握两个概念: 子类型化和类型适应。 这两个概念在软件设计书籍中常见。 1. 子类型化 有一个特定的类型 S

12、, 当且仅当它至少提供了类型 T 的行为, 由称类型 S 是类型 T 的子类型。 或者说类型 T 是类型 S 的子集。 子类型主要存在于类的继承关系, 但不仅限于继承关系。 在 C+继承中, 公有继承可以实现子类型。 例如: class A public: void p() ; class B : public A public: void f() ; 类 B 继承了 类 A, 并且是公有继承方式。 因此, 可以说类 B 是类 A 的一个子类型, 类 B 具备类 A 中的所有行为,或者说类 A 中的所有行为可被用于类 B。 子类型关系是不可逆的。 这就是说, 已知 B 是 A 的子类型, 而认

13、为 A 也是 B 的子类型是错误的, 或者说, 子类型关系是不对称的。 2. 类型适应 回顾 JAVA 语言, JAVA 语言的继承性都是 public 的(实际上 JAVA 语言也不允许 private 和 protected 继承), 也就是说JAVA 语言的子类都是父类的子类型。 JAVA 课本 P89类的赋值相容性 中有一句话: 子类对象即是父类对象, 说的就是子类型和类型适应问题。 类型适应是指两种类型之间的关系。 例如, B 类型适应 A类型是指 B 类型的对象能够用于 A 类型的对象所能使用的场合。 或者说, 可以将子类对象赋值给父类对象, 反过来则不可以。 派生类的对象可以用于

14、基类对象所能使用的场合, 我们说派生类适应于基类。 同样道理, 派生类对象的指针和引用也适应于基类对象的指针和引用。 子类型化与类型适应是一致的。 A 类型是 B 类型的子类型, 那么 A 类型必将适应于 B 类型。 Q4: 理解子类型和类型适应的概念 四、 多继承 1 语法 从多个基类派生的继承称为多继承。 多继承的定义格式如下: class 派生类名:继承方式 1 基类名 1, 继承方式 2 基类名 2, /派生类新定义成员 ; 示例: class MyDate: public Date1,protected Date2 /派生类新定义成员变量或函数 ; /派生类新定义成员变量初始化或函数

15、实现 2 多继承的构造函数 在多继承的情况下, 派生类的构造函数格式如下: 派生类名( 总参数表) :基类名 1 ( 参数表 1 ) , 基类名 2 ( 参数表 2 ) , 子对象名( 参数表 n + 1 ) , / / 派生类构造函数体 其中, 总参数表 中各个参数包含了 其后的各个分参数表。 多继承下派生类的构造函数与单继承下派生类构造函数相似, 它必须同时负责该派生类所有基类构造函数的调用。 3 多继承的二义性问题 一般说来, 在派生类中对基类成员的访问应该是唯一的, 但是, 由于多继承情况下, 可能造成对基类中某成员的访问出现了 不唯一的情况, 则称为对基类成员访问的二义性问题。 例

16、1 : 下面举一个的例子, 对二义性问题进行讨论: class A public: void f(); ; class B public: void f() ; void g(); ; class C : public A, public B public: void g() ; void h() ; ; 如果实例化一个类 C 的对象 c1 , 则对函数 f()的访问 c1 .f(); 便具有二义性: 是访问类 A 中的 f(), 还是访问类 B中的 f()呢? 解决的方法可用以前学过的成员名限定法来消除二义性, 例如: c1 .A:f() ; 或者 c1 .B:f() ; 例 2: 下面再举

17、一个的例子, 对菱型继承的二义性问题进行讨论, 这个问题比较重要: 当一个派生类从多个基类派生类, 而这些基类又有一个共同的基类, 也就是说菱型继承时, 则对该基类中说明的成员进行访问时, 也可能会出现二义性。 例如: class A public: int a; ; class B1 : public A private: int b1 ; ; class B2 : public A private: int b2; ; class C : public B1 , public B2 public: int f(); private: int c; ; 当实例化一个 C 类对象 c1 , 内

18、存是什么状况呢? 对象 c1 是由 2 个 A 对象, 1 个 B1 对象, 1 个 B2 对象,1 个 C 对象组合 而成, 这几个对象不一定占用连续的空间, 它们彼此用指针或引用关联成一个整体。 为什么会有两个 A 对象? 因为 B1 和 B2 各实例化了 一个A 对象。 由于有两个 A 对象存在, 下面的访问都有二义性: c1 .a; c1 .A:a; 而下面的访问是正确的: c1 .B1 :a; c1 .B2:a; 由于二义性的原因, 一个类不可以从同一个类中直接继承一次以上, 例如: class A : public B, public B 这是错误的。 上面的两个例子, 消除二义性

19、的解决办法都不算好, 有没有更好的办法? 答案是使用虚基类。 五、 虚基类 1 虚基类消除二义性 再回到前面例 2 的菱型继承问题, 由类 A, 类 B1 和类B2 以及类 C 组成了 类继承的(菱型) 层次结构。 该结构产生二义性的原因是: 类 C 的对象将包含两个类 A 的子对象(类B1 和类 B2 各实例化出一个类 A 对象) 。 如果要想使这个公共基类在派生类中只产生一个基类子对象, 则必须将这个基类设定为虚基类。 实际上, 引进虚基类的真正目的就是为了 解决二义性问题。 虚基类说明格式如下: virtual 继承方式 基类名 其中, virtual 是虚类的关键字。 虚基类的说明是用

20、在定义派生类时, 写在派生类名的后面。 例 3: class A public: void f() ; protected: int a; ; class B1 : virtual public A protected: int b; ; class B2 : virtual public A protected: int c: ; class C : public B1, public B2 public: int g() ; private: int d; ; 由于使用了 虚基类, 当实例化一个 C 类对象 c1 , 内存是什么状况呢? 对象 c1 是由 1 个 A 对象, 1 个 B1

21、对象, 1 个 B2 对象, 1个 C 对象组合 而成。 为什么只有一个 A 对象? 因为 B1 和 B2 声明 A 为虚基类,保证只产生一个 A 对象, B1 和 B2 各有一个 虚基类指针,指向同一个 A 对象。 这样, 当实例化了 C 类对象 c 后, 内存中只有一个 A 对象,这样消除了 可能出现的二义性。 因此, 下面的引用都是正确的: C c; c. f() ; 对 f() 的引用, 不必区分是类 B1 或类 B2 实例化的类 A 对象,因为类 A 对象只有一个。 同理, 下面两行对 f() 的引用也是正确的。 void C: : g() f() ; 2 回想 JAVA 的多继承

22、JAVA 对多继承的解决办法是简单的: 只允许继承一个实类, 允许继承多个接口, 而接口是不会被实例化的, 这样就避免了 因出现多个父类对象而造成的二义性。 3 虚基类的构造函数 本章前面讲过, 为了 初始化基类的子对象, 派生类的构造函数要调用基类的构造函数。 对于虚基类来讲, 由于派生类的对象中只有一个虚基类子对象, 为保证虚基类子对象只被初始化一次, 这个虚基类构造函数必须只被调用一次。 由于继承结构的层次可能很深, 规定将在建立对象时所指定的类称为最派生类。 C+规定, 虚基类子对象是由最派生类的构造函数通过调用虚基类的构造函数进行初始化的。 如果一条继承链上有多个派生类调用虚基类的构造函数, 编译器保证只能由最派生类调用一次且仅一次。 其它具体规定略去不讲。 4 小结 通过对本章三、 四小节多继承的二义性讨论, 我们看到多继承虽然在思想上更符合现实世界, 但带来的编译器设计、 程序设计方面的复杂性太多, 这也是 JAVA 取消多继承的重要源因。 Q5: 从对象在内存中的映像, 理解菱型继承带来的二义性问题。 Q6: 从对象在内存中的映像, 理解虚基类怎样消除二义性。 本章课后习题: Q1-Q6

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1