java重载与多态

java重载与多态
java重载与多态

多态性是面向对象程序设计代码重用的一个重要机制,我们曾不只一次的提到Java多态性。在Java运行时多态性:继承和接口的实现一文中,我们曾详细介绍了Java实现运行时多态性的动态方法调度;今天我们再次深入Java核心,一起学习Java中多态性的实现。

“polymorphism(多态)”一词来自希腊语,意为“多种形式”。多数Java程序员把多态看作对象的一种能力,使其能调用正确的方法版本。尽管如此,这种面向实现的观点导致了多态的神奇功能,胜于仅仅把多态看成纯粹的概念。

Java中的多态总是子类型的多态。几乎是机械式产生了一些多态的行为,使我们不去考虑其中涉及的类型问题。本文研究了一种面向类型的对象观点,分析了如何将对象能够表现的行为和对象即将表现的行为分离开来。抛开Java中的多态都是来自继承的概念,我们仍然可以感到,Java中的接口是一组没有公共代码的对象共享实现。

多态的分类

多态在面向对象语言中是个很普遍的概念.虽然我们经常把多态混为一谈,但实际上有四种不同类型的多态。在开始正式的子类型多态的细节讨论前,然我们先来看看普通面向对象中的多态。

Luca Cardelli和Peter Wegner("On Understanding Types, Data Abstraction, and Polymorphism"一文的作者,文章参考资源链接)把多态分为两大类----特定的和通用的----四小类:强制的,重载的,参数的和包含的。他们的结构如下:

在这样一个体系中,多态表现出多种形式的能力。通用多态引用有相同结构类型的大量对象,他们有着共同的特征。特定的多态涉及的是小部分没有相同特征的对象。四种多态可做以下描述:

◆强制的:一种隐式做类型转换的方法。

◆重载的:将一个标志符用作多个意义。

◆参数的:为不同类型的参数提供相同的操作。

◆包含的:类包含关系的抽象操作。

我将在讲述子类型多态前简单介绍一下这几种多态。

强制的多态

强制多态隐式的将参数按某种方法,转换成编译器认为正确的类型以避免错误。在以下的表达式中,编译器必须决定二元运算符‘+’所应做的工作:

2.0 + 2.0

2.0 + 2

2.0 + "2"

第一个表达式将两个double的操作数相加;Java中特别声明了这种用法。

第二个表达式将double型和int相加。Java中没有明确定义这种运算。不过,编译器隐式的将第二个操作数转换为double型,并作double型的加法。做对程序员来说十分方便,否则将会抛出一个编译错误,或者强制程序员显式的将int转换为double。

第三个表达式将double与一个String相加。Java中同样没有定义这样的操作。所以,编译器将 double转换成String类型,并将他们做串联。

强制多态也会发生在方法调用中。假设类Derived继承了类Base,类C 有一个方法,原型为m(Base),在下面的代码中,编译器隐式的将Derived类的对象derived转化为Base类的对象。这种隐式的转换使 m(Base)方法使用所有能转换成Base类的所有参数。

1. C c = new C();

2.

3.Derived derived = new Derived();

4.

5. c.m( derived );

并且,隐式的强制转换,可以避免类型转换的麻烦,减少编译错误。当然,编译器仍然会优先验证符合定义的对象类型。

重载的多态

重载允许用相同的运算符或方法,去表示截然不同的意义。‘+’在上面的程序中有两个意思:两个double型的数相加;两个串相连。另外还有整型相加,长整型,等等。这些运算符的重载,依赖于编译器根据上下文做出的选择。以往的编译器会把操作数隐式转换为完全符合操作符的类型。虽然Java明确支持重载,但不支持用户定义的操作符重载。

Java支持用户定义的函数重载。一个类中可以有相同名字的方法,这些方法可以有不同的意义。这些重载的方法中,必须满足参数数目不同,相同位置上的参数类型不同。这些不同可以帮助编译器区分不同版本的方法。

编译器以这种唯一表示的特征来表示不同的方法,比用名字表示更为有效。据此,所有的多态行为都能编译通过。

强制和重载的多态都被分类为特定的多态,因为这些多态都是在特定的意义上的。这些被划入多态的特性给程序员带来了很大的方便。强制多态排除了麻烦的类型和编译错误。重载多态像一块糖,允许程序员用相同的名字表示不同的方法,很方便。

参数的多态

参数多态允许把许多类型抽象成单一的表示。例如,List 抽象类中,描述了一组具有同样特征的对象,提供了一个通用的模板。你可以通过指定一种类型以重用这个抽象类。这些参数可以是任何用户定义的类型,大量的用户可以使用这个抽象类,因此参数多态毫无疑问的成为最强大的多态。

乍一看,上面抽象类好像是java.util.List的功能。然而,Java实际上并不支持真正的安全类型风格的参数多态,这也是java.util.List和java.util的其他集合类是用原始的 https://www.360docs.net/doc/9d3583086.html,ng.Object写的原因(参考我的文章"A Primordial Interface?" 以获得更多细节)。Java的单根继承方式解决了部分问题,但没有发挥出参数多态的全部功能。Eric Allen有一篇精彩的文章“Behold the Power of Parametric Polymorphism”,描述了Java通用类型的需求,并建议给Sun的Java规格需求#000014号文档"Add Generic Types to the Java Programming Language."(参考资源链接)

包含的多态

包含多态通过值的类型和集合的包含关系实现了多态的行为.在包括Java在内的众多面向对象语言中,包含关系是子类型的。所以,Java的包含多态是子类型的多态。

在早期,Java开发者们所提及的多态就特指子类型的多态。通过一种面向类型的观点,我们可以看到子类型多态的强大功能。以下的文章中我们将仔细探讨这个问题。为简明起见,下文中的多态均指包含多态。

面向类型观点

图1的UML类图给出了类和类型的简单继承关系,以便于解释多态机制。模型中包含5种类型,4个类和一个接口。虽然UML中称为类图,我把它看成类型图。如"Thanks Type and Gentle Class," 一文中所述,每个类和接口都是一种用户定义的类型。按独立实现的观点(如面向类型的观点),下图中的每个矩形代表一种类型。从实现方法看,四种类型运用了类的结构,一种运用了接口的结构。

图1:示范代码的UML类图

以下的代码实现了每个用户定义的数据类型,我把实现写得很简单。

用这样的类型声明和类的定义,图2从概念的观点描述了Java指令。

Derived2 derived2 = new Derived2();

图2 :Derived2 对象上的引用

上文中声明了 derived2这个对象,它是Derived2类的。图2种的最顶层把Derived2引用描述成一个集合的窗口,虽然其下的Derived2对象是可见的。这里为每个Derived2类型的操作留了一个孔。Derived2对象的每个操作都去映射适当的代码,按照上面的代码所描述的那样。例如,Derived2对象映射了在Derived中定义的m1()方法。而且还重载了Base类的m1()方法。一个Derived2的引用变量无权访问 Base类中被重载的m1()方法。但这并不意味着不可以用super.m1()的方法调用去使用这个方法。关系到derived2这个引用的变量,这个代码是不合适的。Derived2的其他的操作映射同样表明了每种类型操作的代码执行。

既然你有一个Derived2对象,可以用任何一个Derived2类型的变量去引用它。如图1所示,Derived, Base和IType都是Derived2的基类。所以,Base类的引用是很有用的。图3描述了以下语句的概念观点。

Base base = derived2;

图3:Base类引用附于Derived2对象之上

虽然Base类的引用不用再访问m3()和m4(),但是却不会改变它Derived2对象的任何特征及操作映射。无论是变量derived2还是 base,其调用m1()或

m2(String)所执行的代码都是一样的。

两个引用之所以调用同一个行为,是因为Derived2对象并不知道去调用哪个方法。对象只知道什么时候调用,它随着继承实现的顺序去执行。这样的顺序决定了Derived2对象调用Derived里的m1()方法,并调用Derived2 里的

m2(String)方法。这种结果取决于对象本身的类型,而不是引用的类型。

尽管如此,但不意味着你用derived2和 base引用的效果是完全一样的。如图3所示,Base的引用只能看到Base类型拥有的操作。所以,虽然Derived2有对方法m3()和m4()的映射,但是变量base不能访问这些方法。

运行期的Derived2对象保持了接受m3()和m4()方法的能力。类型的限制使 Base 的引用不能在编译期调用这些方法。编译期的类型检查像一套铠甲,保证了运行期对象只能和正确的操作进行相互作用。换句话说,类型定义了对象间相互作用的边界。

多态的依附性

类型的一致性是多态的核心。对象上的每一个引用,静态的类型检查器都要确认这样的依附和其对象的层次是一致的。当一个引用成功的依附于另一个不同的对象时,有趣的多态现象就产生了。(严格的说,对象类型是指类的定义。)你也可以把几个不同的引用依附于同一个对象。在开始更有趣的场景前,我们先来看一下下面的情况为什么不会产生多态。

多个引用依附于一个对象

图2和图3描述的例子是把两个及两个以上的引用依附于一个对象。虽然Derived2对象在被依附之后仍保持了变量的类型,但是,图3中的Base类型的引用依附之后,其功能减少了。结论很明显:把一个基类的引用依附于派生类的对象之上会减少其能力。

一个开发这怎么会选择减少对象能力的方案呢?这种选择是间接的。假设有一个名为ref的引用依附于一个包含如下方法的类的对象:

用一个Derived2的参数调用poly(Base)是符合参数类型检查的:

方法调用把一个本地Base类型的变量依附在一个引入的对象上。所以,虽然这个方法只接受Base类型的参数,但Derived2对象仍是允许的。开发这就不必选择丢失功能的方案。从人眼在通过Derived2对象时所看到的情况,Base 类型引用的依附导致了功能的丧失。但从执行的观点看,每一个传入poly1(Base)的参数都认为是Base的对象。执行机并不在乎有多个引用指向同一个对象,它只注重把指向另一个对象的引用传给方法。这些对象的类型不一致并不是主要问题。执行器只关心给运行时的对象找到适当的实现。面向类型的观点展示了多态的巨大能力。

附于多个对象的引用

让我们来看一下发生在poly1(Base)中的多态行为。下面的代码创建了三个对象,并通过引用传给poly1(Base):

poly1(Base)的实现代码是调用传进来的参数的m1()方法。图3和图4展示了把三个类的对象传给方法时,面向类型的所使用的体系结构。

图4:将Base引用指向Derived类,以及Base对象

请注意每个图中方法m1()的映射。图3中,m1()调用了Derived类的代码;上面代码中的注释标明了ploy1(Base)调用 Derived.m1()。图4中Derived对象

调用的仍然是Derived类的m1()方法。最后,图4中,Base对象调用的m1()是Base 类中定义的代码。

多态的魅力何在?再来看一下poly1(Base)的代码,它可以接受任何属于Base 类范畴的参数。然而,当他收到一个Derived2的对象时,它实际上却调用了Derived版本的方法。当你根据Base类派生出其他类时,如 Derived,Derived2,poly1(Base)都可以接受这些参数,并作出选择调用合适的方法。多态允许你在完成poly1(Base)后扩展它的用途。

这看起来当然很神奇。基本的理解展示了多态的内部工作原理。在面向类型的观点中,底层的对象所实现的代码是非实质性的。重要的是,类型检查器会在编译期间为每个引用选择合适的代码以实现其方法。多态使开发者运用面向类型的观点,不考虑实现的细节。这样有助于把类型和实现分离(实际用处是把接口和实现分离)。

对象接口

多态依赖于类型和实现的分离,多用来把接口和实现分离。但下面的观点好像把Java的关键字 interface搞得很糊涂。

更为重要的使开发者们怎样理解短语“the interface to an object",典型地,根据上下文,这个短语的意思是指一切对象类中所定义的方法,至一切对象公开的方法。这种倾向于以实现为中心的观点较之于面向类型的观点来说,使我们更加注重于对象在运行期的能力。图3中,引用面板的对象表面被标志成"Derived2 Object"。这个面板上列出了Derived2对象的所有可用的方法。但是要理解多态,我们必须从实现这一层次上解放出来,并注意面向类型的透视图中被标为"Base Reference"的面板。在这一层意思上,引用变量的类型指明了一个对象的表面。这只是一个表面,不是接口。在类型一致的原则下,我们可以用面向类型的观点,为一个对象依附多个引用。对interface to an object这个短语的理解没有确定的理解。

在类型概念中,the interface to an object refers 引用了面向类型观点的最大可能----如图2的情形。把一个基类的引用指向相同的对象缩小了这样的观点----如图3所示。类型概念能使人获得把对象间的相互作用同实现细节分离的要领。相对于一个对象的接口,面向类型的观点更鼓励人们去使用一个对象的引用。引用类型规定了对象间的相互作用。当你考虑一个对象能做什么的时候,只需搞明白他的类型,而不需要去考虑他的实现细节。

Java接口

以上所谈到的多态行为用到了类的继承关系所建立起来的子类型关系。Java接口同样支持用户定义的类型,相对地,Java的接口机制启动了建立在类型层次结构上的多态行为。假设一个名为ref的引用变量,并使其指向一个包含一下方法的类对象:

为了弄明白poly2(IType)中的多态,以下的代码从不同的类创建两个对象,并分别把他们传给 poly2(IType):

上面的代码类似于关于poly1(Base)中的多态的讨论。poly2(IType)的实现代码是调用每个对象的本地版本的m3()方法。如同以前,代码的注释表明了每次调用所返回的CString类型的结果。图5表明了两次调用poly2(IType)的概念结构:

图5:指向Derived2和Separate对象的IType引用

方法poly1(Base)和poly2(IType)中所表现的多态行为的相似之处可以从透视图中直接看出来。把我们在实现在一层上的理解再提高一层,就可以看到这两段代码的技巧。基类的引用指向了作为参数传进的类,并且按照类型的限制调用对象的方法。引用既不知道也不关心执行哪一段代码。编译期间的子类型关系检查保证了通过的对象有能力在被调用的时候选择合适的实现代码。

然而,他们在实现层上有一个重要的差别。在 poly1(Base)的例子中(图3和图4),Base-Derived-Derived2的类继承结构为子类型关系的建立提供了条件,并决定了方法去调用哪段代码。在poly2(IType)的例子中(如图5),则是完全不同的动态发生的。Derived2和Separate不共享任何实现的层次,但是他们还是通过IType的引用展示了多态的行为。

这样的多态行为使Java的接口的功能的重大意义显得很明显。图1中的UML类图说明了Derived是Base和IType的子类型。通过完全脱离实现细节的类型的定义方法,Java实现了多类型继承,并且不存在Java所禁止的多继承所带来的烦人的问题。完全脱离实现层次的类可以按照Java接口实现分组。在图1中,接口IType和Derived,Separate以及这类型的其他子类型应该划为一组。

按照这种完全不同于实现层次的分类方法,Java的接口机制是多态变得很方便,哪怕不存在任何共享的实现或者复写的方法。如图5所示,一个IType的引用,用多态的方法访问到了Derived2和Separate对象的m3()方法。

再次探讨对象的接口

注意图5中的Derived2和Separate对象的对m1()的映射方法。如前所述,每一个对象的接口都包含方法m1()。但却没有办法用这两个对象使方法m1()表现出多态的行为。每一个对象占有一个m1()方法是不够的。必须存在一个可以操作 m1()方法的类型,通过这个类型可以看到对象。这些对象似乎是共享了m1()方法,但在没有共同基类的条件下,多态是不可能的。通过对象的接口来看多态,会把这个概念搞混。

结论

从全文所述的面向对象多态所建立起来的子类型多态,你可以清楚地认识到这种面向类型的观点。如果你想理解子类型多态的思想,就应该把注意力从实现的细节转移到类型的上。类型把对象分成组,并且管理着这些对象的接口。类型的继承层次结构决定了实现多态所需的类型关系。

有趣的是,实现的细节并不影响子类型多态的层次结构。类型决定了对象调用什么方法,而实现则决定了对象怎么执行这个方法。也就是说,类型表明了责任,而负责实施的则是具体的实现。将实现和类型分离后,我们好像看到了这两个部分在一起跳舞,类型决定了他的舞伴和舞蹈的名字,而实现则是舞蹈动作的设计师。

多态参考代码分析

1224: 多态-虚函数问题 Description 请以点类Point为基类派生出一个圆类Circle。Point类的数据成员为x、y(私有属性,存储点的横纵坐标),成员函数有构造函数Point(实现对数据成员x、y的初始化)、输出函数Display(输出点坐标);圆类Circle的数据成员为r(私有属性,存储圆的半径,圆心的点坐标通过继承点类Point加以实现),成员函数有构造函数Circle、计算圆的面积函数Area、计算圆的周长函数Perimeter和输出函数Display,其中构造函数实现基类和圆类的数据成员的初始化,Display函数实现圆心坐标、圆的半径、圆的面积(利用Area函数实现)和圆的周长(利用Perimeter函数实现)的输出。实现Point类和Circle类的定义及成员函数。主函数的代码(不允许改动)如下: int main() { double x,y,r; cin>>x>>y>>r; //圆心的点坐标及圆的半径 Point *p; p=new Point(x,y); p->Display(); p=new Circle(x,y,r); p->Display(); return 0; } 说明:圆周率PI的取值为3.14 提示:Display应设计为虚函数 Input Output Sample Input 0 0 1 Sample Output Point(0,0) Circle's center:Point(0,0) Radius:1 Area:3.14 Perimeter:6.28 **************************************************************** #include using namespace std; const double PI=3.14; class Point { private: double x,y; public:

实验报告类的重载继承和多态

实验报告类的重载继承和多态 篇一:实验三_类的重载继承和多态_() 篇二:实验三_类的重载继承和多态 篇三:C++实验报告(类和对象重载与继承多态和模板异常和流) C++实验报告 目录 C++实验报告 ................................................ ................................................... (4) 实验一:类和对象 ................................................ ................................................... . (4) 1................................................. ................................................... . (4) 代码 ................................................

................................................... .. (4) 运行结果 ................................................ ................................................... (5) 2.................................................. ................................................... (5) 思路: .............................................. ................................................... . (5) 代码 ................................................ ................................................... (5) 运行结果 ................................................ ...................................................

C#中区别多态、重载、重写的概念和语法结构

重写是指重写基类的方法,在基类中的方法必须有修饰符virtual,而在子类的方法中必须指明override。 格式: 基类中: public virtual void myMethod() { } 子类中: public override void myMethod() { } 重写以后,用基类对象和子类对象访问myMethod()方法,结果都是访问在子类中重新定义的方法,基类的方法相当于被覆盖掉了。 重载 用于在给定了参数列表和一组候选函数成员的情况下,选择一个最佳函数成员来实施调用。 public void test(int x,int y){} public void test(int x,ref int y){} public void test(int x,int y,string a){} 重载特征: I.方法名必须相同 II.参数列表必须不相同,与参数列表的顺序无关 III.返回值类型可以不相同 ========================= 但如果有泛型,就要注意了! 多态 c#的多态性主要体现在类的继承上: 子类继承父类的时候,可能出现同名但方法定义不同的情况, 所以在子类中会将原方法覆盖,实现自身的要求. using System; class Employee { virtual public void CalculatePlay() { Console.WriteLine("Employee");

} }; class SalariedEmployee: Employee { override public void CalculatePlay() { Console.WriteLine("Salary"); } }; class PolyApp { public static void Main() { Employee baseE1 = new Employee(); baseE1.CalculatePlay(); Employee baseE2 = new SalariedEmployee(); baseE2.CalculatePlay(); SalariedEmployee s = new SalariedEmployee(); s.CalculatePlay();

习题10-运算符重载与多态性

C 、可以是void 类型 D 、可以是float 类型 A 、运算符重载可以改变运算符的操作数的个数 B 、运算符重载可以改变优先级 C 、运算符重载可以改变结合性 D 、运算符重载不可以改变语法结构 A 、动态联编是以虚函数为基础的 B 、动态联编是在运行时确定所调用的函数代码的 C 、动态联编调用函数操作是指向对象的指针或对象引用 D 、动态联编是在编译时确定操作函数的 A 、虚函数是一个静态成员函数 B 、虚函数是一个非成员函数 C 、虚函数既可以在函数说明时定义,也可以在函数实现时定义 D 、派生类的虚函数与基类中对应的虚函数具有相同的参数个数和类型 B 、重载函数和析构函数 C 、虚函数和对象 习题 10 运算符重载与多 态性 、单项选择题 A 、 ? : B 、 [ ] C 、 new D 、 && 2、 下列运算符不能用友元函数重载的是( )。 A 、 + B 、 = C 、 * D 、 << 3、 在一个类中可以对一个操作符进行( )重载。 A 、 1种 B 、 2 种以下 C 、 3 种以下 D 、 多种 4、友元运算符 obj1>obj2 被 C++ 编译器解释为( )。 1、下列运算符中,( )运算符在C++中不能重载。 A 、 operator > (obj1, obj2) B 、 > (obj1, obj2) C 、 obj2.operator > (obj1) D 、 obj1.operator > (obj2) 5、下列关于C++运算符函数的返回类型的描述中,错误的是( ) 。 A 、可以是类类型 B 、可以是int 类型 6、下列关于运算符重载的描述中,正确的是( )。 9、对虚函数的调用( ) 。 A 、 定使用动态联编 B 、必须使用动态联编 C 、 定使用静态联编 D 、不一定使用动态联编 10、编译时的多态性可以通过使用( )获 得。 7、下列关于动态联编的描述中,错误的是( )。 8、关于虚函数的描述中,正确的是( )。 A 、虚函数和指针 D 、虚函数和引用

实验8 多态性与虚函数

实验八多态性与虚函数 一、实验目的和要求 1.了解多态的概念; 2.了解虚函数的作用及使用方法; 3.了解静态关联和动态关联的概念和用法; 4.了解纯虚函数和抽象类的概念和用法 二、实验内容和结果 1.阅读下面的程序 1.1请写出程序的执行结果,并在上机时对照理解 class Vehicle {public: void run() const { cout << "run a vehicle. "<

airplane.run(); cout<<"(b) 用指向基类的指针访问成员函数: "<run(); vp=&airplane; vp‐>run(); } 1.2 如果将Vehicle 类的定义修改为虚函数,其余不变,请写出程序的执行结果,并在上机时对照理解 class Vehicle {public: virtual void run() const { cout << "run a vehicle. "<

继承与多态的习题

一:选择题 1. 下面有关析构函数的说法中,不正确的是( ) A.析构函数中不可包含Return语句 B.一个类中只能有一个析构函数 C.用户可定义有参析构函数 D.析构函数在对象被撤销时,被自动调用 2.派生类不可以访问基类的( ) A.Public成员B.Private成员 C.Protected成员D.Protected internel成员 3.有关sealed修饰符,描述正确的是( ) A.密封类可以被继承 B.abstract修饰符可以和sealed修饰符一起使用 C.密封类不能实例化 D.使用sealed修饰符可保证此类不能被派生 4.若想从派生类中访问基类的成员,可以使用( ) A.this关键字B.me关键字 C.base关键字D.override关键字 5.下面有关派生类的描述中,不正确的是( ) A.派生类可以继承基类的构造函数 B.派生类可以隐藏和重载基类的成员 C.派生类不能访问基类的私有成员 D.派生类只能有一个直接基类 6.C#中,继承具有( ),即A类派生B类,B类又派生C类,则C类会继承B类中的成员和A类中的成员。 A.传递性B.多态性C.单继承D.多继承 7.下面有关静态方法的描述中,错误的是( ) A.静态方法属于类,不属于实例 B.静态方法可以直接用类名调用 C.静态方法中,可以定义非静态的局部变量 D.静态方法中,可以访问实例方法 8.下面关于运算符重载的描述中,错误的是( ) A.重载的运算符仍然保持其原来的操作数个数、优先级和结合性不变 B.可以重载双目运算符,不可以重载单目运算符 C.运算符重载函数必须是public的 D.运算符重载函数必须是static的 9.下面对派生类和基类的关系的描述中,不正确的是( ) A.派生类的方法可以和基类的方法同名 B.派生类是对基类的进一步扩充 C.派生类也可作另一个派生类的基类 D.派生类继承基类的公有、保护和私有成员 10.下面关于虚方法的描述中,正确的是() A.虚方法可以实现静态联编 B.在一个程序中,不能有同名的虚方法 C.虚方法必须是类的静态成员

java多态

Java多态 1. Java中除了static和final方法外,其他所有的方法都是运行时绑定的。private 方法都被隐式指定为final的,因此final的方法不会在运行时绑定。当在派生类中重写基类中static、final、或private方法时,实质上是创建了一个新的方法。 2.在派生类中,对于基类中的private方法,最好采用不同的名字。 3.包含抽象方法的类叫做抽象类。注意定义里面包含这样的意思,只要类中包含一个抽象方法,该类就是抽象类。抽象类在派生中就是作为基类的角色,为不同的子类提供通用的接口。 4.对象清理的顺序和创建的顺序相反,当然前提是自己想手动清理对象,因为大家都知道Java垃圾回收器。 5.在基类的构造方法中小心调用基类中被重写的方法,这里涉及到对象初始化顺序。 6.构造方法是被隐式声明为static方法。 7.用继承表达行为间的差异,用字段表达状态上的变化。 在JAVA中有两种多态是指:运行时多态和编译时多态。 关于类的多态性简介如下: 多态(polymorphism)意为一个名字可具有多种语义.在程序设计语言中,多态性是指”一种定义,多种实现”.例如,运算符+有多种含义,究竟执行哪种运算取决于参加运算的操作数类型: 1+2 //加法运算符 “1” + “2” //字符串连接运算,操作数是字符串 多态性是面向对象的核心特征之一,类的多态性提供类中成员设计的灵活性和方法执行的多样性. 1、类多态性表现 (1)方法重载 重载表现为同一个类中方法的多态性.一个类生命多个重载方法就是为一种功能提供多种实现.编译时,根据方法实际参数的数据类型\个数和次序,决定究竟应该执行重载方法中的哪一个. (2)子类重定义从父类继承来的成员 当子类从父类继承来的成员不适合子类时,子类不能删除它们,但可以重定义它们,使弗雷成员适应子类的新需求.子类重定义父类成员,同名成员在父类与子类之间表现出多态性,父类对象引用父类成员,子类对象引用子类成员,不会产生冲突和混乱. 子类可重定义父类的同名成员变量,称子类隐藏父类成员变量.子类也可以重定义父类的同名成员方法,当子类方法的参数列表与父类方法参数列表完全相同时,称为子类方法覆盖

c++运算符重载和多态性实验报告

实验5 运算符重载和多态性班级学号(最后两位)姓名成绩 一、实验目的 1.掌握用成员函数重载运算符的方法 2.掌握用友元函数重载运算符的方法 3.理解并掌握利用虚函数实现动态多态性和编写通用程序的方法 4.掌握纯虚函数和抽象类的使用 二、实验内容 1.复数类加减法乘除运算(用成员函数定义运算符重载)。 复数类的定义: class complex //复数类声明 { public: //外部接口 complex(double r=0.0,double i=0.0) //构造函数 {real=r,imag=i;} complex operator +(complex c2); //运算符"+"重载成员函数 complex operator - (complex c2); //运算符"-"重载成员函数 complex operator *(complex ); //运算符"*"重载成员函数 complex operator /(complex); //运算符"/"重载成员函数 complex operator =(complex c2); //运算符"="重载成员函数 void display(); //输出复数 private: //私有数据成员 double real; //复数实部 double imag; //复数虚部 }; 实验代码: #include using namespace std; class Complex { public: Complex() { real=0; imag=0; } Complex(double r,double i) { real=r; imag=i; } Complex operator+(Complex &c2); Complex operator-(Complex &c2); Complex operator*(Complex &c2); Complex operator/(Complex &c2); void display(); private: double real; double imag; };

继承、多态,重载、重写的区别与总结

继承、多态,重载、重写的区别与总结 李氏原则: (1)父类的引用指向子类对象。 -多态 (2)接口的引用指向实现类的实例。-接口 Polo咯, 什么是多态?它的实现机制是什么呢?重载和重写的区别在那里?这就是这一次我们要回顾的四个十分重要的概念:继承、多态、重载和重写 继承(inheritance) 简单的说,继承就是在一个现有类型的基础上,通过增加新的方法或者重定义已有方法(下面会讲到,这种方式叫重写)的方式,产生一个新的类型。继承是面向对象的三个基本特征--封装、继承、多态的其中之一,我们在使用JAVA 时编写的每一个类都是在继承,因为在JAVA语言中,https://www.360docs.net/doc/9d3583086.html,ng.Object类是所有类最根本的基类(或者叫父类、超类),如果我们新定义的一个类没有明确地指定继承自哪个基类,那么JAVA就会默认为它是继承自Object类的。 我们可以把JAVA中的类分为以下三种: 类:使用class定义且不含有抽象方法的类。 抽象类:使用abstract class定义的类,它可以含有,也可以不含有抽象方法。接口:使用interface定义的类。 在这三种类型之间存在下面的继承规律: 类可以继承(extends)类,可以继承(extends)抽象类,可以继承(implements)接口。 抽象类可以继承(extends)类,可以继承(extends)抽象类,可以继承(implements)接口。 接口只能继承(extends)接口。 请注意上面三条规律中每种继承情况下使用的不同的关键字extends和implements,它们是不可以随意替换的。大家知道,一个普通类继承一个接口后,必须实现这个接口中定义的所有方法,否则就只能被定义为抽象类。我在这里之所以没有对implements关键字使用“实现”这种说法是因为从概念上来说它也是表示一种继承关系,而且对于抽象类implements接口的情况下,它并不是一定要实现这个接口定义的任何方法,因此使用继承的说法更为合理一些。 以上三条规律同时遵守下面这些约束: 类和抽象类都只能最多继承一个类,或者最多继承一个抽象类,并且这两种情况是互斥的,也就是说它们要么继承一个类,要么继承一个抽象类。 类、抽象类和接口在继承接口时,不受数量的约束,理论上可以继承无限多个接

11多态性与虚函数

第10章多态性与虚函数 【内容提要】 多态性的概念; 函数和运算符的重载; 虚函数和抽象类。 【重点与难点】 10.1 多态性的概念 在面向对象的概念中,多态性是指不同对象接收到相同消息时,根据对象类的不同产生不同的动作。 由静态联编支持的多态性称为编译时的多态性或静态多态性,也就是说,确定同名操作的具体操作对象的过程是在编译过程中完成的。C++用函数重载和运算符重载来实现编译时的多态性。 由动态联编支持的多态性称为运行时的多态性活动太多态性,也就是说,确定同名操作的具体操作对象的过程是在运行过程中完成的。C++用继承和虚函数来实现运行时的多态性。 10.2 函数和运算符的重载 10.2.1 函数重载 面向对象程序设计中,函数的重载表现为两种情况:第一种是参数个数或类型有所差别的重载,第二种是函数的参数完全相同但属于不同的类。 10.2.2 运算符重载 C++预定义的运算符只是对基本数据类型进行操作,而对于自定义的数据类型比如类,却没有类似的操作。为了实现对自定义类型的操作,就必须自己编写程序来说明某个运算符作用在这些数据类型上时,应该完成怎样的操作,这就要引入运算符重载的概念。 运算符的重载形式有两种,一种是重载为类的成员函数,一种是重载为类的友元函数。 成员运算符函数的定义: 在类内声明的一般形式为: <返回类型> operator<运算符>(参数表); 在类外定义的一般形式为: <返回类型> <类名∷> operator<运算符>(参数表) { 函数体 } 其中,operator是定义运算符重载函数的关键字;运算符是要重载的运算符的名称;参数表给出重载运算符所需要的参数和类型。 将重载的运算符函数定义为类的友元函数,称为友元运算符函数。友元运算符函数不 友员运算符函数的定义: 在类内声明的一般形式为: friend<返回类型> operator<运算符>(参数表); 在类外定义的一般形式为: <返回类型> operator<运算符>(参数表) { 函数体 } 其中,friend是声明友元函数的关键字,operator是定义运算符重载函数的关键字;运 算符是要重载的运算符的名称;参数表给出重载运算符所需要的参数和类型。 几种典型运算符的重载

c++多态性与虚函数习题

作业题 一、写出下列程序运行结果 1.#include using namespace std; class A { public: virtual void func( ) {cout<<”func in class A”< using namespace std; class A{ public: virtual ~A( ){ cout<<”A::~A( ) called “<

}; void fun(A *a) { delete a; } int main( ) { A *a=new B(10); fun(a); } 二、程序设计题 1有一个交通工具类vehicle,将它作为基类派生小车类car、卡车类truck和轮船类boat,定义这些类并定义一个虚函数用来显示各类信息。 5.2定义一个shape抽象类,派生出Rectangle类和Circle类,计算各派生类对象的面积Area( )。 5.5某学校对教师每月工资的计算公式如下:固定工资+课时补贴。教授的固定工资为5000元,每个课时补贴50元;副教授的固定工资为3000元,每个课时补贴30元;讲师的固定工资为2000元,每个课时补贴20元。给出教师抽象类及主函数,补充编写程序求若干教师的月工资。 #include using namespace std; class Teacher{ protected: double salary; int workhours; public: Teacher(int wh=0){workhours=wh;} virtual void cal_salary()=0; void print(){cout<cal_salary(); prof.print(); Vice_Prof vice_prof(250); pt=&vice_prof; pt->cal_salary(); vice_prof.print(); Lecture lecture(100); pt=&lecture; pt->cal_salary(); lecture.print (); return 0; }

java多态性

Java的多态性 面向对象编程有三个特征,即封装、继承和多态。 封装隐藏了类的内部实现机制,从而可以在不影响使用者的前提下改变类的内部结构,同时保护了数据。 继承是为了重用父类代码,同时为实现多态性作准备。那么什么是多态呢? 方法的重写、重载与动态连接构成多态性。Java之所以引入多态的概念,原因之一是它在类的继承问题上和C++不同,后者允许多继承,这确实给其带来的非常强大的功能,但是复杂的继承关系也给C++开发者带来了更大的麻烦,为了规避风险,Java只允许单继承,派生类与基类间有IS-A的关系(即“猫”is a “动物”)。这样做虽然保证了继承关系的简单明了,但是势必在功能上有很大的限制,所以,Java引入了多态性的概念以弥补这点的不足,此外,抽象类和接口也是解决单继承规定限制的重要手段。同时,多态也是面向对象编程的精髓所在。 要理解多态性,首先要知道什么是“向上转型”。 我定义了一个子类Cat,它继承了Animal类,那么后者就是前者是父类。我可以通过 Cat c = new Cat(); 实例化一个Cat的对象,这个不难理解。但当我这样定义时: Animal a = new Cat(); 这代表什么意思呢? 很简单,它表示我定义了一个Animal类型的引用,指向新建的Cat类型的对象。由于Cat是继承自它的父类Animal,所以Animal类型的引用是可以指向Cat类型的对象的。那么这样做有什么意义呢?因为子类是对父类的一个改进和扩充,所以一般子类在功能上较父类更强大,属性较父类更独特, 定义一个父类类型的引用指向一个子类的对象既可以使用子类强大的功能,又可以抽取父类的共性。 所以,父类类型的引用可以调用父类中定义的所有属性和方法,而对于子类中定义而父类中没有的方法,它是无可奈何的;

c++多态性与虚函数习题答案

多态性与虚函数 1.概念填空题 1.1 C++支持两种多态性,分别是编译时和运行时。 1.2在编译时就确定的函数调用称为静态联编,它通过使用函数重载,模板等实现。 1.3在运行时才确定的函数调用称为动态联编,它通过虚函数来实现。 1.4虚函数的声明方法是在函数原型前加上关键字virtual。在基类中含有虚函数,在派生类中的函数没有显式写出virtual关键字,系统依据以下规则判断派生类的这个函数是否是虚函数:该函数是否和基类的虚函数同名;是否与基类的虚函数参数个数相同、类型;是否与基类的虚函数相同返回类型。如果满足上述3个条件,派生类的函数就是虚函数。并且该函数覆盖基类的虚函数。 1.5 纯虚函数是一种特别的虚函数,它没有函数的函数体部分,也没有为函数的功能提供实现的代码,它的实现版本必须由派生类给出,因此纯虚函数不能是友元函数。拥有纯虚函数的类就是抽象类类,这种类不能实例化。如果纯虚函数没有被重载,则派生类将继承此纯虚函数,即该派生类也是抽象。 3.选择题 3.1在C++中,要实现动态联编,必须使用(D)调用虚函数。 A.类名 B.派生类指针 C.对象名 D.基类指针 3.2下列函数中,不能说明为虚函数的是(C)。 A.私有成员函数 B.公有成员函数 C.构造函数 D.析构函数 3.3在派生类中,重载一个虚函数时,要求函数名、参数的个数、参数的类型、参数的顺序和函数的返回值(A)。 A.相同 B.不同 C.相容 D.部分相同 3.4当一个类的某个函数被说明为virtual时,该函数在该类的所有派生类中(A)。 A.都是虚函数 B.只有被重新说明时才是虚函数 C.只有被重新说明为virtual时才是虚函数 D.都不是虚函数 3.5(C)是一个在基类中说明的虚函数,它在该基类中没有定义,但要求任何派生类都必须定义自己的版本。 A.虚析构函数B.虚构造函数 C.纯虚函数D.静态成员函数 3.6 以下基类中的成员函数,哪个表示纯虚函数(C)。 A.virtual void vf(int);B.void vf(int)=0; C.virtual void vf( )=0;D.virtual void vf(int){ } 3.7下列描述中,(D)是抽象类的特性。 A.可以说明虚函数 B.可以进行构造函数重载 C.可以定义友元函数 D.不能定义其对象 3.8类B是类A的公有派生类,类A和类B中都定义了虚函数func( ),p是一个指向类A对象的指针,则p->A::func( )将(A)。

实验6多态性与虚函数

[实验目的] 1、了解多态性的概念; 2、了解虚函数的用途及使用方法; 3、了解纯虚函数和抽象类的概念和用法。 [实验要求] 给出以下各实验内容的源程序代码,并把编译、运行过程中出现的问题以及解决方法填入实验报告中,按时上交。 [实验学时] 2学时。 [实验内容] 1、写一个程序,定义抽象基类Shape,由它派生出3个派生类:Circle(圆形)、Square(正方形)、Rectangle(矩形)。利用指针、虚函数printArea()分别输出以上三者的面积,3个图形的数据在定义对象时给定。 [源程序] #include using namespace std; class Shape { public: virtual float area()const=0; virtual void display()const=0; }; class Circle:public Shape { public: Circle(double a):r(a){} virtual float area()const{return 3.14*r*r;} virtual void display()const { cout<<"圆面积"<

class Rectangle:public Shape { public: Rectangle(double a,double b):l(a),w(b){} virtual float area()const{return l*w;} virtual void display()const { cout<<"矩形面积"<display(); m=m+p[i]->area(); }

C++语言中多态性的分析

第13卷第1期广州航海高等专科学校学报 Vol .13 No .1 2005年6月 JOURNAL OF G UANGZ HOU MAR I TI M E COLLEGE Jun .2005 文章编号:1009-8526(2005)01-0055-03 C++语言中多态性的分析 江勇驰 (广州航海高等专科学校交通运输管理系,广东广州510725) 摘 要:通过分析C++语言多态性的各种形式、特征及其在程序设计中的应用,说明多态性是对相似问题求解的有效方法. 关键词:C++;多态性;面向对象;虚函数中图分类号:TP311.1 文献标识码:A  收稿日期:2005-03-10  作者简介:江勇驰(1972-),男,助理馆员,主要从事自动化与计算机的实验教学和管理. C++语言具有数据封装、继承及多态性三大特征[1] ,多态性同时考虑了类的数据封装与继承关系的设计,是C++最重要的特征.在程序中多态性是指同一符号或名字在不同情况下代表不同但相似的功能[2] ,是对相似问题的一种求解方法.本文主要分析C++语言的多态性支持在程序设计中的应用. 1 实现形式及其应用 在C++面向对象程序设计中,实现多态性有两 种基本形式:编译时多态性和运行时多态性[3] .编译时多态性是指在程序编译阶段即可确定下来的多态性,包括强制多态和重载多态两种形式.运行时多态性是指必须等到程序动态运行时才可确定的多态性,主要通过继承结合动态绑定获得,分别体现在包含多态与类型参数化多态两方面. 1.1 强制多态 当表达式中存在不同类型的操作数时,为对此表达式进行求值,编译程序需要对其中的部分操作数强制进行类型转换,以保证运算符两边的操作数类型一致,但体现出来的却是完成了不同类型操作数间的运算,这就是C++中的强制多态性.C++中,“1+2”表示整数的加法,“1.0+2.0”表示浮点数的相加,而“1.0+2”需要作类型转换,实际进行浮点数加法,但却表示整数与浮点数的相加.这两种运算的内部实现是不同的,而且效率相差很大,不便用不同的符号表达.同一个运算符“+”可以同时具有不 同但相似的运算含义,体现的就是强制多态性 [3] . C++中规定了基本数据类型之间的转换规则:如果 运算符两边的操作数类型运算不一致,则总是将取值范围的最大值较小的类型转换为取值范围的最大值较大的类型,取值范围从大到小分别是:l ong double,double,fl oat,unsigned l ong,l ong,unsigned 和int .正是由于这种多态性,才简化了程序设计,可以 按相同的逻辑处理各种数据的加法.不过C ++中,强制多态性是有限的,不能用这一方法去解决许多类似问题.重载多态提供了另外一种途径. 1.2 重载多态 重载多态主要通过使用重载机制获得,包括函数重载和运算符重载两大类. 运算符重载允许重新定义C ++语言已有的运算符,以一种更加自然的方式使用自己定义的类类型.例如,加法运算符“+”表示整数或浮点数的加法,如果用户定义了一个复数类Comp lex,在类定义中对加法运算符“+”进行了重载,定义实现复数相加的操作Co mp lex operat or +(const Comp lex&oth 2er ){...},那么使用复数类声明两个对象Comp lex obj1,obj2;后,就可以直接使用obj1+obj2表示对 复数求和的操作,使得程序语言更接近习惯用法.C++语言中颇具典型的例子就是插入符“<<”和 提取符“>>”的重载.在C 语言中,利用库函数 p rintf ()输出或scanf ()输入时需要用参数指明输出或输入的数据类型方可正确输出或输入,而在C++

4.多态

多态 0.英语单词 1.为什么使用多态 1)问题的由来 需求变更 宠物系统中添加主人给宠物喂食功能,具体需求如下: ?给Dog喂食,其健康值增加3,输出吃饱的信息 ?给Penguin喂食,其健康值增加5,输出吃饱的信息 分析 1)给抽象类Pet增加抽象方法eat()方法; 2)让Dog类重写Pet类的eat()方法,实现狗狗吃饭功能; 3)让Penguin类重写Pet类的eat()方法,实现企鹅吃饭功能 4)创建主人类Master,添加feed(Dog dog)方法,调用Dog类的eat()方法,实现狗狗的 喂养;添加feed(Penguin penguin)方法,调用Penguin类的eat()方法,实现企鹅的喂 养。 5)创建测试类Test,在类中创建主人、狗狗和企鹅对象,调用相应方法实现主人喂养 宠物的功能 实例1:Pet.java Dog.java Penguin.java Master.java Test.java 从实例的运行结果看,我们已经顺利实现了主人给宠物喂食的功能,但是,如果主人又领养了一只猫或更多的宠物,该如何实现给宠物喂食呢? 当然你可以添加一个Cat类,让其继承Pet类,重写eat()方法;然后在Master类中重载feed()方法,添加一个feed(Cat cat)方法。可是,如果添加更多的宠物呢,这样做的缺点是:代码频繁修改(每次都要修改Master类,添加feed()的重载方法),可扩展性、可维护性差(如果领养的宠物过多,Master类中就会有很多重载的feed()方法)。 2)怎么解决问题 如果能实现如下效果就好了:Master类中只有一个feed()方法,可以实现多有宠物的喂食,不管领养多少宠物,都不用修改Master类源码。 答案是肯定的,通过多态实现该效果。

西华大学C++实验报告5 多态和虚函数、运算符重载

. 西华大学实验报告(计算机类) 开课学院及实验室:机械工程与自动化 实验时间 : 年 月 日 一、实验目的 1. 理解虚函数的特性; 2. 理解纯虚函数和抽象类的特性; 3. 掌握用虚函数实现运行时的多态性和编写通用程序的方法; 4. 掌握用成员函数和友元函数重修运算符的方法。 二、内容与设计思想 上机实践内容: 1. 定义一个抽象类CShape ,包含纯虚函数Area ()(原来计算面积)和SetData ()(原 来重设形状大小)。然后派生出三角形CTriangle 类、矩形CRect 类、圆CCircle 类,分别求其面积。最后定义一个CArea 类,计算这几个形状的面积之和,个形状的数据通过CArea 类构造函数或成员函数来设置。编程一个完整的程序。 2. 定义一个复数类CComplex ,通过重载运算符“*”和“/”,直接实现两个复数之间的 乘除运算。编写一个完整的程序(包括测试运算符的程序部分)。运算符“*”用成员函数实现重载,而运算符“/”用友元函数实现重载。 提示:两复数相乘的计算公式为:(a+bi)*(c+di)=(ac-bd)+ad+bc)i; 两复数相除的计算公式为:(a+bi)/(c+di)=(ac+bd)/(c*c+d*d)+(bc-ad)/(c*c+d*d)i 三、使用环境 操作系统:Windowns XP C++环境:Visual C++ 6.0

四、核心代码及调试过程 #include using namespace std; class CShape { public: virtual float Area()=0; virtual void SetData(float f1,float f2)=0; }; class CTriangle:public CShape { public: CTriangle(float h=0,float w=0) { H=h; W=w;} float Area() { return(float)(H*W*0.5); } void SetData(float f1,float f2) { H=f1; W=f2; } private: float H,W; }; class CRect:public CShape { public: CRect(float h=0,float w=0) { H=h; W=w; } float Area()

C++实验多态性实验报告

贵州大学实验报告 学院:电子信息学院专业:通信工程班级: 姓名学号实验组5实验时间指导教师成绩 实验项目名称多态性 实 验通过让学生进行实验,使其对于动态多态性有一个较为深入的了解和熟悉。最终可以目熟练使用。 的 实 1.编写 4 个重载函数 Double( x),返回值为输入参数的两倍;参数类型分别为int 、验long 、float 、 double ,返回值类型与参数类型一样。 2.请编写一个抽象类Shape,在此基础上派生出类Rectangle和Circle,二者都有 要 计算对象面积的函数GetArea ()和计算周长函数GetPerim ()。 求3.对类 Point 重载 ++(自增)、 -- (自减)运算符。 实 验Visual C++的编译环境下,独立完成实验要求的内容,独立完成编写、编译以及运行 原的过程 理 实 验 安装了 Visual C++ 的 PC机器 仪 器 实 验 按照实验要求的内容逐一完成实验的要求。顺序是编写、编译、运行。 步 骤

实 1. 编写 4 个重载函数 Double(x),返回值为输入参数的两倍;参数类型分别为int、 验 long 、 float 、 double ,返回值类型与参数类型一样。 2. 请编写一个抽象类Shape,在此基础上派生出类Rectangle 和 Circle,二者都有计 内算对象面积的函数GetArea ()和计算周长函数GetPerim ()。容 3. 对类 Point 重载 ++(自增)、 -- (自减)运算符。 1、代码如下: #include using namespace std; int Double(int x); long Double(long x); float Double(float x); double Double(double x); int main() { int myInt = 6500; cout< #define PI 3.1415926; using namespace std; class Shape // 抽象类的定义 { public: virtual double GetArea()= 0; //纯虚函数

相关文档
最新文档