热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

类的继承(很全面)

1.继承介绍上一篇中,已介绍了复合类,复合类只是C++中创建复杂类的主要方法之一。本篇中,介绍另外一种方法,即类的继承。与复合类通过结合和连接其他对象进行创建新的对象;继承,通过直接获取其他对象的属

1. 继承介绍

上一篇中,已介绍了复合类,复合类只是C++中创建复杂类的主要方法之一。

本篇中,介绍另外一种方法,即类的继承。

与复合类通过结合和连接其他对象进行创建新的对象;继承,通过直接获取其他对象的属性和行为来创建新的对象,同时对其进行扩展和特殊化。

如你继承你父母的基因;技术的更新换代;C++语言继承了C语言的许多特性,同时也增加了自己的特性等等。

父类(基类)-被继承的对象;子类(衍生类)-继承形成的对象。

继承的关系是一种is-a关系,即子类可以当作父类进行处理;

通常,子类继承了父类的所有属性,子类可以定义或重定义继承得来的属性,增加新的属性,甚至隐藏属性。

C++中需要继承的原因:

1)OOP的最基本的特性之一就是代码的重用性;但是已有代码通常不能恰好满足需要,通常的方法是修改已有代码,但是这不是最好的办法。

2)较好的方法之一,是拷贝已有的代码,然后进行修改;但这方法有许多缺点:A)很危险,因为拷贝代码会出现错漏;B)修改已有代码,你必须从根本上去理解已有的代码,如果已有代码很复杂就很困难了;C)修改和修正代码之后,必须维持原有代码的同步,维护量很大。

3)所以,继承在大多数的问题中,是一个有效的方法;继承允许你利用现有的代码,达到自己的要求。

2. C++基本继承

继承在C++中,通常出现在类之间。当一个类继承其他的类,衍生类就会继承父类的变量和函数;这些变量和函数就成为这个类的一部分。

一个Person类:

Person类包含了人的基本信息;

一个BaseballPlayer类:

BaseballPlayer也需要包含姓名、年龄和性别等信息;但刚才已定义了一个Person类,所以BaseballPlayer可以继承Person类:

现在,测试一下衍生类BaseballPlayer:

一个Emploee衍生类:

继承链:

通过继承链,我们可以定义一系列的可重用的代码。

小结:通过继承,我们可以获取父类的信息,而不用去重新定义;当父类发生改变,子类也会相应改变;

3. 衍生类的构造顺序

在上一节中,类可以从其他类中继承变量和函数;这节主要讲衍生类的构造顺序。

下面是2个新类:

Drived类包含了2部分:一部分是基类,一部分是衍生类的。

在实例化中:

Base是非衍生类,只需单单执行自己的默认构造函数;Drived是继承Base的衍生类,实例化的过程中,需执行Base和自己的构造部分;

衍生类在构造过程中,遍历继承树,构造每个继承的部分。

现在,以下演示了衍生类的构造顺序:

打印的结果是:

如以上所示,衍生类构造时,首先是构造基类部分;毕竟没有父类,不存在子类;子类使用父类中的变量和函数,而父类不了解子类,首先构造基类,保证了子类可以使用已初始化的变量。

2)继承链中的构造顺序

打印的结果是:

如上所示,继承链中的构造顺序,是居于最高层的基类首先构造,逐步沿着继承树构造。

4. 衍生类的构造函数和初始化

本章节主要详细讲解衍生类的构造函数和初始化的规则。

在上一节中:

对于Base的实例化,简单就如:

因为Base是非衍生类,只需考虑自己的成员的初始化;

构造的过程是:

A)为cBase分配内存空间;B)合适的Base构造函数调用;C)初始化列表初始化变量;D)执行构造函数;E)返回给调用者。

而对于Drived的实例化,如:

构造过程是:

A)为cDrive分配内存空间,足够承载Base部分和Drived部分;B)合适的Drived构造函数调用;C)Base对象首先构造;

D)初始化列表初始化变量;E)执行构造函数;F)返回给调用者。

衍生类和非衍生类的主要区别就是,衍生类必须首先构造基类对象。

2)初始化基类变量

在刚才的Drived的实例化中,没有提供对基类变量的初始化,如需对m_nValue和m_dValue同时赋值

新手或许会这样:

但是,C++防止了在衍生类的初始化列表中,直接对基类的变量进行初始化。即基类的变量必须在基类的初始化列表中初始化,非继承变量可以在衍生类的初始化列表初始化。

这样做的理由:假如需初始化的变量是常量,常量必须在定义时赋值;如果在构造基类完成后,再在衍生类的初始化列表中初始化,或许出现改变了常量的值。

C++这样做,使所有的变量只执行一次初始化过程。

另一种错误的做法:

在构造函数体中,执行对基类的变量赋值;这样也出现常量或引用的第二次赋值。

C++提供了选择合适的基类构造函数进行对基类的初始化。

正确的做法:

构造上顺序是:

A)cDrive分配内存空间;C)Drived(double,int)构造函数调用,dValue=5.0, nValue=5;D)基类构造函数Base(int)调用,m_nValue=5 ;

E)基类构造函数执行返回;F)衍生类将其m_dValue=5.0 ;G)衍生类构函数执行返回。

现在,再次回顾一下2个类:

使用本章节的方法,BaseballPlayer如下:

测试:

小结:

衍生类的析构函数执行顺序和构造函数执行顺序相反;

通常,基类的首先执行初始化,但也给予我们选择基类的构造函数;

使用基类构造函数执行初始化,提供了代码的可维护性和减少重复性。

5. 继承与访问限制符

在以上的章节中,所涉及的数据成员都是共有的,者章节将讲述继承过程中的访问限制符。

public成员变量可以在任何对象中访问;而private成员变量只能在类的成员函数中访问;即衍生类不可以访问基类的私有成员变量。

如:

在处理继承类时,较为复杂:

1)第三个访问限制符-protected,限制访问成员函数,无论是自己的还是上一级的继承类的。

2)衍生类的访问限制符会根据继承方式的不同而改变;3种继承方式-public、private、protected,即公有/私有/保护继承。

由3种成员访问标志符和3种继承类型,构成了9种组合;

请记住以下3个基本原则:

1)类中,可以访问任何成员,不管其访问标志符;

2)对类成员的公共访问,主要基于类的访问标志符;

3)衍生类对基类成员的访问基于继承的类型,衍生类内可以对其增加的成员可以访问。

公有继承

公有继承是大多时候使用的继承类型;所有基类的成员保持其访问限制;

公有继承规则:

A)衍生类中,不可以直接访问基类的私有成员;

B)衍生类中,可以直接访问基类的保护成员;但不是将其公开化,公有继承类的对象不可以访问保护成员。

概括起来,就是公有继承中,基类的成员访问限制在衍生类中不变。

私有继承

私有继承,所有基类的成员将是作为私有成员;即基类的私有成员依然是私有,而公有和保护成员变成了私有。

私有继承,不影响衍生类对基类成员的访问,只是影响类通过衍生类对基类成员的访问限制。

保护继承

保护继承,几乎不会用到,除非是特殊的情况。

保护继承,基类的私有成员依然是私有,保护成员依然是保护,而公有的成员变成了私有的。

小结:

1)基类保持其成员的访问限制;

2)衍生类保持其对基类成员的访问限制;衍生类的继承方式不会影响该访问;

3)衍生类通过不同的继承类型,可以改变对基类成员的访问限制。

6. 在衍生类中增加、修改和隐藏成员

在以上章节中曾提到,继承的最重要之一是代码的重用;你可以在衍生类中对基类的函数进行修改和隐藏,可以增加自己的函数功能等。

1)增加新的功能

在如下的类中:

在Drived类中增加一个GetValue的函数:

2)重定义功能:

如下:

都输出一样的结果:我是一个Base。

现在,在Derived中修改基类的Identity函数:

再次测试,输出的结果是:

重定义的函数,其返回值、函数名称和返回值都必须和基类的相应的函数一致;但衍生类没有继承基类对应函数的访问限制,即衍生类可以改变重定义函数的访问限制;

3)增加现有的函数:

可以在衍生类的重定义函数中,通过::来调用基类对应的函数。

4)隐藏函数:

如上所示,衍生类可以隐藏或公开基类的某些成员;但是,衍生类不可以将基类的私有成员公有化或保护化

7. 多重继承

目前涉及的继承都是单一继承,C++提供了多重继承的功能。

例如:一个老师是一个人,同时也是一个职工;这样,老师可以同时继承人和职工。

多重继承的基类之间,使用逗号隔开。

多重继承的问题

多重继承增加了程序的复杂程度,增加了维护的困难。

1)歧义性:多重的基类包含相同名称的函数;

但是,可以通过明确的函数调用:

2)钻石问题:

因为Scanner和Printer都继承PowerDevice,Copier协调不了Scanner和Printer的功能。

大多时候,通过显示调用来解决。

通过以上2个问题,多重继承的问题都可以通过单一继承来解决;所以像Java和C#不支持多重继承,因为多重继承增加了代码程序的复杂程度。

其实,在有些情况下,多重继承是很好的解决方法。

8. 虚拟基类

在第7节中,多重继承出现的钻石问题,本章节继续该主题。

在以下:

如果实例化Copier:

因为钻石问题,会出现公有基类的构造2次:

可以通过virtual关键字,实现公有基类的一份拷贝:

当公有的基类只有一个拷贝,由Copier负责公有基类的创建;

再次测试:

需要留意:

1)虚拟基类在非虚拟基类创建之前创建;

2)继承虚拟基类的类依然是调用虚拟基类的构造函数;

3)如果一个类继承了一个或多个继承于虚拟基类的类时,最后衍生的类负责创建虚拟基类。

 

【免责特此声明:
1)本内容可能是来自互联网的,或经过本人整理的,仅仅代表了互联网和个人的意见和看法!
2)本内容仅仅提供参考,任何参考该内容造成任何的后果,均与原创作者和本博客作者无关!】


推荐阅读
  • 在处理 XML 数据时,如果需要解析 `` 标签的内容,可以采用 Pull 解析方法。Pull 解析是一种高效的 XML 解析方式,适用于流式数据处理。具体实现中,可以通过 Java 的 `XmlPullParser` 或其他类似的库来逐步读取和解析 XML 文档中的 `` 元素。这样不仅能够提高解析效率,还能减少内存占用。本文将详细介绍如何使用 Pull 解析方法来提取 `` 标签的内容,并提供一个示例代码,帮助开发者快速解决问题。 ... [详细]
  • 使用 ListView 浏览安卓系统中的回收站文件 ... [详细]
  • 在当前的软件开发领域,Lua 作为一种轻量级脚本语言,在 .NET 生态系统中的应用逐渐受到关注。本文探讨了 Lua 在 .NET 环境下的集成方法及其面临的挑战,包括性能优化、互操作性和生态支持等方面。尽管存在一定的技术障碍,但通过不断的学习和实践,开发者能够克服这些困难,拓展 Lua 在 .NET 中的应用场景。 ... [详细]
  • 本文详细解析了Java类加载系统的父子委托机制。在Java程序中,.java源代码文件编译后会生成对应的.class字节码文件,这些字节码文件需要通过类加载器(ClassLoader)进行加载。ClassLoader采用双亲委派模型,确保类的加载过程既高效又安全,避免了类的重复加载和潜在的安全风险。该机制在Java虚拟机中扮演着至关重要的角色,确保了类加载的一致性和可靠性。 ... [详细]
  • 如何使用 `org.apache.tomcat.websocket.server.WsServerContainer.findMapping()` 方法及其代码示例解析 ... [详细]
  • 如何使用 `org.eclipse.rdf4j.query.impl.MapBindingSet.getValue()` 方法及其代码示例详解 ... [详细]
  • 在C#编程中,设计流畅的用户界面是一项重要的任务。本文分享了实现Fluent界面设计的技巧与方法,特别是通过编写领域特定语言(DSL)来简化字符串操作。我们探讨了如何在不使用`+`符号的情况下,通过方法链式调用来组合字符串,从而提高代码的可读性和维护性。文章还介绍了如何利用静态方法和扩展方法来实现这一目标,并提供了一些实用的示例代码。 ... [详细]
  • 在对WordPress Duplicator插件0.4.4版本的安全评估中,发现其存在跨站脚本(XSS)攻击漏洞。此漏洞可能被利用进行恶意操作,建议用户及时更新至最新版本以确保系统安全。测试方法仅限于安全研究和教学目的,使用时需自行承担风险。漏洞编号:HTB23162。 ... [详细]
  • 提升视觉效果:Unity3D中的HDR与Bloom技术(高动态范围成像与光线散射)
    提升视觉效果:Unity3D中的HDR与Bloom技术(高动态范围成像与光线散射) ... [详细]
  • Spring框架中枚举参数的正确使用方法与技巧
    本文详细阐述了在Spring Boot框架中正确使用枚举参数的方法与技巧,旨在帮助开发者更高效地掌握和应用枚举类型的数据传递,适合对Spring Boot感兴趣的读者深入学习。 ... [详细]
  • 本文深入解析了WCF Binding模型中的绑定元素,详细介绍了信道、信道管理器、信道监听器和信道工厂的概念与作用。从对象创建的角度来看,信道管理器负责信道的生成。具体而言,客户端的信道通过信道工厂进行实例化,而服务端则通过信道监听器来接收请求。文章还探讨了这些组件之间的交互机制及其在WCF通信中的重要性。 ... [详细]
  • 基于试题数据的智能化管理平台采用Java语言进行面向对象编程,旨在构建一个高效的试题信息管理系统。该系统在JDK 6.0和MyEclipse 10.6环境下开发,通过优化试题数据管理和处理流程,提升系统的稳定性和用户体验。平台支持多用户操作,具备强大的数据处理能力和灵活的扩展性,适用于各类教育机构和考试组织。 ... [详细]
  • 如何在C#中配置组合框的背景颜色? ... [详细]
  • 如何精通编程语言:全面指南与实用技巧
    如何精通编程语言:全面指南与实用技巧 ... [详细]
  • 具备括号和分数功能的高级四则运算计算器
    本研究基于C语言开发了一款支持括号和分数运算的高级四则运算计算器。该计算器通过模拟手算过程,对每个运算符进行优先级标记,并按优先级从高到低依次执行计算。其中,加减运算的优先级最低,为0。此外,该计算器还支持复杂的分数运算,能够处理包含括号的表达式,提高了计算的准确性和灵活性。 ... [详细]
author-avatar
thofarq
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有