继承和多态——学习总结

继承

一、

1.所谓继承,也就是在已有类的基础上创建新类的过程,适当的使用继承可以节省代码量,优化整个程序的结构。

2.类继承关系的语法形式:    

 class 派生类名 : 基类名表    

 {           

数据成员和成员函数声明 

 };

基类名表  构成:

 访问控制  基类名1, 访问控制  基类名2 ,… , 访问控制  基类名n

不论哪种方式继承,派生类都不能直接使用基类的私有成员。

派生类的生成过程

派生类的生成过程经历了三个步骤:        

●吸收基类成员(全部吸收(构造、析构除外),但不一定可见)      

 ●改造基类成员 (通过在派生类中定义同名成员(包括成员函数和数据成员)来屏蔽(隐藏)在派生类中不起作用的部分基类成员)      

●添加派生类新成员

定义派生类对象时,只是复制基类的空间但是没有赋值

二、

1.重名成员

 派生类定义了与基类同名的成员,在派生类中访问同名成员时屏蔽了基类的同名成员  

在派生类中使用基类的同名成员,显式地使用类名限定符: 类名 :: 成员

2.派生类中访问静态成员

 基类定义的静态成员,将被所有派生类共享(基类和派生类共享基类中的静态成员);

 根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质;

 派生类中访问静态成员,用以下形式显式说明:类名 :: 成员     或通过对象访问    对象名 . 成员

基类的初始化

在创建派生类对象时用指定参数调用基类的构造函数来初始化派生类继承基类的数据

  派生类构造函数声明为:

派生类构造函数 ( 变元表 ) : 基类 ( 变元表 ) , 对象成员1( 变元表 )

         … 对象成员n ( 变元表 ) ;  

构造函数执行顺序:基类 -> 对象成员-> 派生类

派生类构造函数和析构函数的定义规则

  1. 基类的构造函数和析构函数不能被继承
  2. 如果基类没有定义构造函数或有无参的构造函数, 派生类也可以不用定义构造函数
  3. 如果基类无无参的构造函数,派生类必须定义构造函数
  4. 如果派生类的基类也是派生类,则每个派生类只负责直接基类的构造
  5. 派生类是否定义析构函数与所属的基类无关    

析构函数的执行顺序与构造函数的执行顺序完全相反  

在C++中,派生类构造函数的一般格式为:

派生类::派生类名(参数总表):基类名(参数表)
     {
             // 派生类新增成员的初始化语句
     }

注意:这是基类有构造函数且含有参数时使用

多继承

1.一个类有多个直接基类的继承关系称为多继承

2.多继承声明语法:

class  派生类名 : 访问控制  基类名1 ,  访问控制  基类名2 ,  … , 访问控制  

基类名n    

{        

 数据成员和成员函数声明

};

3. 类 C 可以根据访问控制同时继承类 A 和类 B 的成员,并添加自己的成员

4.多继承方式下构造函数的执行顺序:

多继承方式下构造函数的执行顺序:

●先执行所有基类的构造函数

●再执行对象成员的构造函数

●最后执行派生类的构造函数

处于同一层次的各基类构造函数的执行顺序取决于定义派生类时所指定的基类顺序;与派生类构造函数中所定义的成员初始化列表顺序没有关系。 内嵌对象成员的构造函数执行顺序与对象在派生类中声明的顺序一致

赋值兼容规则:

赋值兼容规则指在程序中需要使用基类对象的任何地方,都可以用公有派生类的对象来替代。

赋值兼容规则中所指的替代包括以下的情况:  

  •  派生类的对象可以赋给基类对象
  •  派生类的对象可以初始化基类的引用  
  •  派生类的对象的地址可以赋给基类类型的指针

 根据赋值兼容规则, 以下几种情况是合法的: 

  • 可以用派生类对象给基类对象赋值。
  • 可以用派生类对象来初始化基类的引用。
  • 可以用派生类对象来初始化基类的引用。
  • 可以把指向派生类对象的指针赋值给指向基类对象的指针。

赋值兼容规则的特点:

●在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。

在实际使用的过程中,我们要根据具体的问题需求设计类,然后再分析这些类里面是否有重复的部分,有的话则将重复的部分提取出来形成基类,然后再在需要的子类中进行继承。

虚函数和多态性

1.多态性(Polymorphism)是指一个名字,多种语义;或界面相同,多种实现。

2.重载函数是多态性的一种简单形式。  

3.虚函数允许函数调用与函数体的联系在运行时才进行,称为动态联编。

4.联编分成两大类:静态联编和动态联编。

静态联编优点:调用速度快,效率高,但缺乏灵活性;动态联编优点:运行效率低,但增强了程序灵活性。 C++为了兼容C语言仍然是编译型的,采用静态联编。为了实现多态性,利用虚函数机制,可部分地采用动态联编。

多态的实现:

多态从实现的角度来讲可以划分为两类:编译时的多态和运行时的多态。

编译时的多态是通过静态联编来实现的。静态联编就是在编译阶段完成的联编。编译时多态性主要是通过函数重载和运算符重载实现的。

运行时的多态是用动态联编实现的。动态联编是运行阶段完成的联编。运行时多态性主要是通过虚函数来实现的。

静态联编

静态联编,是程序的匹配、连接在编译阶段实现,也称为早期匹配。

  •    重载函数使用静态联编。

普通成员函数重载可表达为两种形式:

1. 在一个类说明中重载。

2. 基类的成员函数在派生类重载。有 3 种编译区分方法:

(1)根据参数的特征加以区分

(2)使用“ :: ”加以区分

(3)根据类对象加以区分

动态联编 

动态联编是指程序联编推迟到运行时进行,所以又称为晚期联编。

  •  switch 语句和 if 语句是动态联编的例子。

实现动态联编方式的前提:

●先要声明虚函数

●类之间满足赋值兼容规则

●通过指针与引用来调用虚函数。

根据赋值兼容,用基类类型的指针指向派生类,就可以通过这个指针来使用类(基类或派生类)的成员函数。 如果这个函数是普通的成员函数,通过基类类型的指针访问到的只能是基类的同名成员。 而如果将它设置为虚函数,则可以使用基类类型的指针访问到指针正在指向的派生类的同名函数。从而实现运行过程的多态。

虚函数

  冠以关键字 virtual 的成员函数称为虚函数

实现运行时多态的关键首先是要说明虚函数,另外,必须用   基类指针调用派生类的不同实现版本

虚函数和基类指针

 基类指针虽然获取派生类对象地址,却只能访问派生类从基类继承的成员。

注意:  一个虚函数,在派生类层界面相同的重载函数都保持虚特性  ;虚函数必须是类的成员函数 ;不能将友元说明为虚函数,但虚函数可以是另一个类的友元 ;析构函数可以是虚函数,但构造函数不能是虚函数。

虚函数的重载特性

  •  在派生类中重载基类的虚函数要求函数名、返回类型、参数个数、参数类型和顺序完全相同
  •  如果仅仅返回类型不同,C++认为是错误重载  
  • 如果函数原型不同,仅函数名相同,丢失虚特性

虚析构函数

  •  构造函数不能是虚函数。建立一个派生类对象时,必须从类层次的根开始,沿着继承路径逐个调用基类的构造函数  
  • 析构函数可以是虚的。虚析构函数用于指引 delete 运算符正确析构动态对象

说明:

1.派生类应该从它的基类公有派生。

2.必须首先在基类中定义虚函数。

3.派生类对基类中声明虚函数重新定义时,关键字virtual可以不写。

4.一般通过基类指针访问虚函数时才能体现多态性。

5.一个虚函数无论被继承多少次,保持其虚函数特性。

6.虚函数必须是其所在类的成员函数,而不能是友元函数,也不能是静态函数。

7.构造函数、内联成员函数、静态成员函数不能是虚函数。 (虚函数不能以内联的方式进行处理)

8.析构函数可以是虚函数,通常声明为虚函数。

纯虚函数和抽象类

纯虚函数是一个在基类中说明的虚函数,在基类中没有定义, 要求任何派生类都定义自己的版本,为各派生类提供一个公共界面。

纯虚函数说明形式:     

 virtual  类型  函数名(参数表)= 0 ;

 一个具有纯虚函数的基类称为抽象

  1. 定义抽象类不能直接生成对象,但是可以生成指针和引用
  2. 派生抽象类时,一定要重写纯虚函数

虚函数与多态的应用:

  •  虚函数和多态性使成员函数根据调用对象的类型产生不同的动作  
  • 多态性特别适合于实现分层结构的软件系统,便于对问题抽象时    定义共性,实现时定义区别

    在上学期的java中已经接触过继承和多态了,而且之前也学过这些,但是因为缺乏练习所以很多东西并没有掌握得很透彻。继承和多态是面向对象程序设计比较重要的一部分,因为之前学过这些,所以这学期学起来对我来说相对来说难度减小了很多。

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(程序设计B)