`

学习笔记之继承与派生

阅读更多

面向对象程序设计有4个主要特点:抽象,封装,继承和多态。

1.1继承与派生的概念

在C++中可重用性是通过继承(inheritance)这一机制来实现的。继承是C++的一个只要组成部分.

一个新类从已有的父类那里获得其已有特征,这种现象称为类的继承。通过继承,一个新建子类从已有的父类那里获得父类的特性。

从另一个角度说,从已有的类(父类)产生一个新的子类,称为类的派生。

类的继承是用已有的类来简历专用类的编程技术.

派生类继承了基类的所有数据成员和成员函数,并可以对成员做必要的增加和调整。

一个基类可以派生出多个派生类,每一个派生类又可以作为基类再派生出新的派生类,因此基类和派生类是相对而言的.

一个派生类不仅可以从一个基类派生,也可以从多个基类派生。一个派生类有两个或者多个基类的称为多重继承(multiple inheritance)。

关于派生类和基类的关系,可以表述为:派生类是基类的具体化,而基类则是派生类的抽象。

继承方式包括:public(公用的),private(私有的),protected(保护的).此项是可选的,如果不写此项,则默认是private的。

派生类从基类接收成员,派生类把基类全部的成员(不包括构造函数和析构函数)接手过来,也就是说是没有选择的,不能选择接收其中一部分成员,而舍弃另一部分成员.

不同继承方式决定了基类成员在派生类中的访问属性.

1)公用继承(public inheritance)

基类的公用成员和保护成员在派生类中保持原有访问属性,其私有成员仍为基类私有。

2)私有继承(private inheritance)

基类的公有成员和保护成员在派生类中成了私有成员,其私有成员仍为基类私有。

3)受保护的继承(protected inheritance)

基类的公用成员和保护成员在派生类中成了保护成员,其私有成员仍为基类私有.

公用继承:

采用公用继承方式时,基类的公用成员和保护成员在派生类中仍然保持其公用成员和保护成员的属性,而基类的私有成员在派生类中并没有成为派生类的私有成员,它仍然是基类的私有成员,只有基类的成员函数可以应用它,而不能被派生类的成员函数应用,因此就成为派生类中的不可访问的成员。

私用继承

私有基类的公用成员和保护成员在派生类中的访问属性相当于派生类中的私有成员,即派生类的成员函数能访问他们,而在派生类不能访问他们。私有基类的私有成员在派生类中成为不可访问的成员,只有基类的成员函数可以应用他们.一个基类成员在基类中的访问属性和在派生类中的访问属性可能是不同的.

保护继承

如果基类声明了私有成员,那么任何派生类都是不能访问他们的,若希望在派生类中能访问他们,应当把它们声明为保护成员,如果一个类中声明了保护成员,就意味着该类可能要用作基类,在他的派生类中会访问这些成员

比较一下私有继承和保护继承,可以发现,在直接派生类中,以上两种继承方式的作用实际上是相同的;在类外不能访问任何成员,而在派生类中可以通过成员函数访问基类中的公用成员和保护成员。

在派生类中,成员有4种不同的访问属性:

1)公用的,派生类和派生类外都可以访问。

2)受保护的,派生类内可以访问,派生类外不能访问,其下一层的派生类可以访问。

3)私有的,派生类内可以访问,派生类外不能访问

4)不可访问的,派生类内和派生类外都不能访问。

派生类的构造函数和析构函数

用户在声明类时可以不定义构造函数,系统会自动设置一个默认的构造函数,在定义类对象时会自动调用这个默认的构造函数。这个构造函数实际上是一个空函数,不执行任何操作,如果需要对类中的数据成员初始化,应自己定义构造函数。

构造函数的主要作用是对数据成员初始化,在设计派生类的构造函数时,不仅要考虑派生类所新增的数据成员的初始化,还应当考虑基类的数据成员初始化,也就是说,希望在执行派生类的构造函数时,使派生类的数据库成员和基类的数据成员同时都被初始化,解决这个问题的思路是:在执行派生类的构造函数时,调用基类的构造函数。

其一般形式为

派生类构造函数名(总参数列表):基类构造函数名(参数类别)

{派生类中新增数据成员初始化语句}

请注意:在类中对派生类构造函数作声明时,不包括基类构造函数名及其参数列表,只在定义函数时才将他们列出。

实际上,在派生类构造函数中对基类成员初始化,就是构造函数初始化表,也就是说,不仅可以利用初始化表对构造函数的数据成员初始化,而且可以利用初始化表调用派生类的基类构造函数,实现对基类数据成员的初始化。也可以在同一个构造函数的定义中同时实现这两种功能。

在建立一个对象时,执行构造函数的顺序是:派生类构造函数先调用基类的构造函数,再执行派生类构造函数本身(即派生类构造函数的函数体)

在派生类对象释放时,先执行派生类的析构函数,再执行基类的析构函数。

派生类构造函数的任务应该包括3个部分

对基类数据成员的初始化,对子对象数据成员初始化,对派生类数据成员初始化

程序中派生类构造函数首部如下:

Student1(int n,string nam,int n1,string nam1,int a,string ad):Student(n,nam),monotor(n1,nam1)

归纳起来,定义派生类构造函数的一般形式为

派生类构造函数名(总参数表列):基类构造函数名(参数表列),子对象名(参数表列)

{派生类中新增数据成员初始化语句}

执行派生类构造函数的顺序是:

调用基类构造函数,对基类数据成员初始化

调用子对象构造函数,对子对象数据成员初始化

再执行派生类构造函数本身,对派生类数据成员初始化。

派生类构造函数的总参数表列中的参数,应当包括基类构造函数和子对象的参数列表中的参数,基类构造函数和子对象的次序可以是任意的。如上面的派生类构造函数首部可以写成

Student(int n,string nam,int n1,string nam1,int a,stirng ad):monitor(n1,nam1),Student(n,nam)

如果在基类中没有定义构造函数,或者定义了没有参数的构造函数,那么在定义派生类构造函数时可以不写基类构造函数,因为此时派生类构造函数没有向基类构造函数传递参数的任务。调用派生类构造函数时系统自动首先调用基类的默认构造函数。如果基类和子对象类型的声明中都没有定义带参数的构造函数,而且也不需对派生类自己的数据成员初始化,则可以不必显示的定义派生类构造函数。因为此时派生类构造函数既没有像基类构造函数和子对象构造函数传递参数的任务,也没有对派生类数据成员初始化的任务。在建立派生类对象时,系统会自动调用系统提供的派生类的默认构造函数,并在执行派生类默认构造函数的过程中,调用基类的默认构造函数和子对象类型默认构造函数。

如果在基类或者子对象类型的声明中定义了才参数的构造函数,那么就必选显示的定义派生类构造函数,并在派生类构造函数中写出基类或子对象类型的构造函数及其参数。

如果在基类中既定义无参的构造函数,又定义了有参的构造函数(构造函数重载),则在定义派生类构造函数时,既可以包含基类构造函数及其参数,也可以不包含基类构造函数,在调用派生类构造函数时,根据构造函数的内容决定调用基类的有参构造函数还是无参构造函数。

派生类的析构函数

在执行派生类的析构函数时,系统会自动调用基类的析构函数和子对象的析构函数,对基类和子对象进行清理。调用的顺序和构造函数正好相反;先执行派生类自己的析构函数,对派生类新增加的成员进行清理,然后调用子对象的析构函数,对子对象进行清理,最后调用基类的析构函数,对基类进行清理.

声明多重继承的方法

如果已声明了类A,类B和类C,可以声明多重继承的派生类D:

class D:public A,private B,protected C

{类D新增加的成员}

多重继承派生类的构造函数形式于单继承时的构造函数形式基本相同,只有在初始表中包含多个基类构造函数

派生类构造函数名(总参数列表):基类1构造函数(参数类别),基类2构造函数(参数列表),基类3构造函数(参数列表)

{派生类中新增成员初始化}

各基类的排列顺序任意。派生类构造函数同样为:先调用基类的构造函数,再执行派生类构造函数的函数体.调用基类构造函数的顺序是按照声明派生类时基类出现的顺序.

多重继承一起的二义性问题

虚基类

虚基类的作用: 如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员,在引用这些同名成员时,必选在派生类对象名后增加直接基类名,以避免产生二义性,使其唯一地标识一个成员.

在一个类中 保留间接共同基类的多份同名成员,这种现象是人们不希望出现的。

C++提供虚基类(virtual base class)的方法,使得在继承间接共同基类时只保留一份成员.

现在,将类A声明为虚基类,方法如下

class A//声明基类A

{......};

class B:virtual public A//声明类B是类A的公用派生类,A是B的虚基类

{......};

class C:virtual public A//声明类C是类A的公用派生类,A是C的虚基类

{......};

注意:虚基类并不是声明基类时声明的,而是在声明派生类时,指定继承方式时声明的。因为一个基类可以在生成一个派生类时作为虚基类,而在声明另一个派生类时不作为虚基类,声明虚基类的一般形式为

class 派生类名:virtual 继承方式 基类名

经过这样的声明后,当基类通过多条派生路径被一个派生类继承时,该派生类只继承该基类一次。

需要注意:为了保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中声明为虚基类,否则仍然会出现对基类的多次继承.

虚基类的初始化

C++编译系统只执行最后的派生类对虚基类的构造函数的调用,而忽略虚基类的其他派生类(如类B和类C)对虚基类的构造函数的调用,这就保证了虚基类的数据成员不会被多次初始化.

注意:在定义派生类的构造函数时,与以往使用的方法有所不同。规定:在最后的派生类中不仅仅要负责对其直接基类进行初始化,还要负责对虚基类初始化

使用多重继承时要十分小心,经常会出现二义性问题。许多专业人员认为:不要提倡在程序中使用多重继承,只有在比较简单和不易出现二义性的情况下或实在必要时才使用多重继承,能用单一继承解决的问题不要使用多重继承,也是由于这个原因,有些面向多谢的程序设计语言(如java,smalltalk)并不支持多重继承.

基类与派生类的转换

基类于派生类对象之间有赋值兼容关系,由于派生类中包含从基类继承的成员,因此可以将派生类的值赋给基类对象,在用到基类对象的时候可以其子类对象代替.

1)派生类对象可以向基类对象赋值。

A a1;//定义基类A对象a1

B b1;//定义类A的公用派生类B的对象b1

a1 = b1;//用派生类B对象b1对基类对象a1赋值

在赋值时舍弃派生类自己的成员,实际上,所谓赋值只是对数据成员赋值,对成员函数不存在赋值问题。

请注意:赋值后不能企图通过对象a1去访问派生类对象b1的成员,因为b1的成员于a1的成员是不同的。

应当注意,只能用子类对象对其基类对象赋值,而不能用基类对象对其之类对象赋值,理由是显然的,因为基类对象不包括派生类的成员,无法对派生类的成员赋值

2)派生类对象可以提到基类对象向基类对象的引用进行赋值或者初始化.

如已定义了基类A对象a1,可以定义a1的引用变量

A a1;//定义基类A对象a1

B b1;//定义公用派生类B对象b1

A &r = a1;//定义基类A对象的引用变量r,并哦那个a1对其初始化

这时,引用变量r是a1的别名,r和a1共享同一段储存单元。也可以用子类对象初始化引用变量r,将上面最后一行改为:

A &r = b1;//定义基类A对象的引用变量r,并用派生类B对象b1对其初始化

或者保留上面第3行"A &r = a1;",而对r重新赋值:

r = b1;//用派生类B对象b1对a1的引用变量r赋值

3)如果函数的参数是基类对象或者基类对象的引用,相应的实参可以用子类对象

4)派生类对象的地址可以赋给指向基类对象的指针变量,也就是说,指向基类对象的指针变量也可以指向派生类对象.

用指向基类对象的指针变量指向子类对象是合法的,安全的,不会出现遍以上的错误,但在应用上却不能完全满足人们的希望,人们有时希望通过使用基类指针能够基类和子类对象的成员。在下一章就要解决这个问题。办法是使用虚函数和多态性

分享到:
评论

相关推荐

    C++远征之继承篇学习笔记之一

    在C++编程语言中,继承是一种重要的面向对象编程(OOP)特性,它允许我们创建一个新的类(派生类),该类继承了现有类(基类)的特性和行为。通过继承,我们可以重用代码,提高代码的可维护性和可扩展性。本文将详细...

    C++学习笔记——继承和组合

    根据给定的文件标题、描述、标签以及部分内容,本文将深入探讨C++中的两个核心概念:继承与组合。这两种机制都是面向对象编程中用于实现代码重用的重要手段,但它们在设计模式、实现细节以及适用场景上存在显著差异...

    Java学习笔记(必看经典).pdf

    本知识点的标题为“Java学习笔记(必看经典)”,意味着所整理的内容是针对Java初学者的一系列核心概念和原理的总结。 首先,面向对象编程是Java语言的核心,它与传统的面向过程编程有显著的不同。面向对象编程强调的...

    P127~134C++类和对象继承学习笔记.docx

    C++类和对象继承学习笔记 本文档主要讲解C++类和对象继承的基本语法和应用,通过继承技术减少重复代码,实现代码的重用和简洁。 继承是面向对象三大特性之一,用于描述类与类之间的特殊关系。例如,在实现网页中的...

    VC++深入详解学习笔记

    **VC++深入详解学习笔记** 在编程领域,Visual C++(简称VC++)是Microsoft公司开发的一款集成开发环境,主要用于编写使用C++语言的应用程序。它不仅提供了编译器,还包括了调试器、资源编辑器等工具,为开发者提供...

    c++ 学习笔记

    综上所述,C++的学习笔记涵盖了继承的基本概念、访问控制、对象空间布局以及多态性的重要实现机制。理解并熟练掌握这些知识点对于深入学习C++和开发面向对象的软件至关重要。通过实际编程练习和分析,可以更深刻地...

    python基础学习笔记整理

    2.2.2、类的继承与派生 2.2.3、封装、反射、单例、元类 2.2.4、模块编程(工具类) 2.3、网络编程 2.4、并发编程 2.5、操作数据库 2.5.1、mysql 2.5.2、redis 2.5.3、MongoDB 2.6、API接口 3、Java调用Python

    C++自考笔记(下)

    C++是一种强大的面向对象编程语言,它支持继承和派生机制,这是面向对象编程的核心特性。在本篇C++自考笔记中,我们将深入探讨这两个概念。 **继承和派生的概念** 继承允许我们创建一个新的类(派生类),这个新类...

    MFC学习笔记

    ### MFC学习笔记知识点解析 #### 一、Win32基本程序概念与消息处理机制 在探讨MFC之前,我们先来了解下Win32基本程序概念中的关键知识点:消息处理机制。 **消息处理机制**是Windows编程的核心之一。Windows操作...

    C面向对象编程基础学习笔记PPT学习教案.pptx

    本篇笔记主要探讨了C#中面向对象编程的基础概念,特别是类的继承和成员。 首先,继承是面向对象编程中的一个关键概念,它允许我们创建一个新类(派生类),这个新类可以从现有的类(基类)继承特性。在C#中,一个...

    C & C++学习笔记集合

    6. **继承与多态**:派生类、虚函数、抽象类和接口,以及多态性的实现。 7. **模板**:泛型编程,包括函数模板和类模板。 8. **异常处理**:try-catch 语句用于错误处理。 9. **STL 使用**:容器、迭代器的使用,...

    c++学习笔记.pdf

    - **保护继承(Protected Inheritance)**:保护继承时,基类的所有公有成员和保护成员都成为派生类的保护成员,仅能被派生类成员函数或友元访问。 文档中通过具体的代码示例进一步阐述了不同类型的继承。例如,`...

    net学习笔记及其他代码应用

    接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类,它...

    [学习笔记]C++程序设计教程第二版[钱能].doc

    根据提供的文件信息,我们可以推断出这是一份关于C++程序设计的学习资料,具体来说是《C++程序设计教程第二版》这本书的学习笔记。虽然给定的部分内容仅包含了一些图片占位符和数字(可能是指页码),但我们可以基于...

    C# 语法 学习 笔记

    以上就是C#语法学习笔记中涉及的一些核心概念。理解并熟练运用这些概念对于编写高质量的C#代码至关重要。在实际编程中,还会涉及到接口(Interface)、委托(Delegate)、事件(Event)、异常处理(Exception ...

    Qt技术的个人学习笔记

    ### Qt技术的个人学习笔记 #### 一、C++基础知识概览 1. **C++简介**: - **定义**:C++是一种基于C语言的面向对象编程语言,它继承了C语言的所有特性,并在此基础上增加了面向对象的特性。 - **特性**:C++支持...

    java学习笔记,好好学习

    这篇“java学习笔记”可能包含了从基础到进阶的各种Java编程概念和技术。以下是对这些笔记可能涵盖的一些关键知识点的详细说明: 1. **Java基础知识**: - **语法**:包括变量声明、数据类型(如整型、浮点型、...

    C++学习笔记

    - `protected`:保护成员,只能被类内方法以及派生类访问。 4. **成员的初始化**:类中的私有成员需要通过公有方法或者构造函数进行初始化。 #### 四、构造函数与析构函数 1. **构造函数**: - 构造函数用于...

    c++培训课程详细学习笔记

    这份"C++培训课程详细学习笔记"是深入理解和掌握C++语言的重要资源,旨在帮助初学者及有经验的开发者巩固基础,提升技能。 一、C++基础 1. 变量与数据类型:C++支持基本数据类型(如int、float、char)以及构造数据...

Global site tag (gtag.js) - Google Analytics