分类:
2007-05-09 17:45:50
一﹑什么是多态?为什么要支持多态?
多态是一种普遍存在的现象,如water的三种形态:冰﹑水﹑汽,又如算术运算1+1, 1+0.5, 1/2+0.5等。
多态性用一句经典的英文来解释就是:a value can belong to multiple types.
那么在中支持多态有什么好处呢?首先,可以使程序中的数学运算符合常规的数学运算规则,为程序提供更强的表达能力;其次使得对不同类型的数据有同样的操作语义,可以实现程序的重用,而重用标识的资源,可以提高程序的可读性和可理解性。
系统支持多态的前提就是能够静态(编译时)或者动态(运行时)地确定类型。
二﹑多态的分类有哪些?
多态分为两种:通用的多态(universal)和特定的多态(ad hoc)。两者的区别是前者对工作的类型不加限制,允许对不同类型的值执行相同的代码;后者只对有限数量的类型有效,而且对不同类型的值可能要执行不同的代码。
通用的多态又分为参数多态(parametric)和包含多态(inclusion);特定的多态分为过载多态(overloading)和强制多态(coercion)。2.1参数多态
概念:采用参数化模板,通过给出不同的类型参数,使得一个结构有多种类型。
例如:Ada中的generic(类属 )。
分析:这两个packages的:
·应用内涵一致:提供的都是堆栈抽象操作。
·实现结构相似:采用同类,实现代码相同。
·元素的类型不同,一些用常量表示的细节不同。
对比:函数是相似的语句序列的一种涵义明确的抽象,用函数中的语句表示相同的部分,用形参指明不同的部分,用实参体现不同的部分。但是,函数的参数只能是数据,不能是类型。
类属在一个抽象结构中允许以参量形式来表示可变的类型﹑函数﹑常数﹑数据值,在编译时(静态)进行实例化,结果是一个具体的结构(类型﹑函数等)。而且类型的实例化可以静态进行,也可以动态进行,但结果都是一个值。
·对实参所取得类型不加限制。
·对不同的实参执行的是相同的代码。
因而构成了多态。
2.2 包含多态
概念:同样的操作可用于一个类型及其子类型。(注意是子类型,不是子类。)包含多态
一般需要进行运行时的类型检查。如Pascal中的子界。
几点需要注意的地方:
1. 包含多态的操作存在着逆单调(Anti-mornotonic)。即一个类型t上的操作,当其定义域缩小成t的一个子类型时,其值域应不小于t.
2.3 过载多态
概念:同一个名(操作符﹑函数名)在不同的上下文中有不同的类型。程序设计语言中基本类型的大多数操作符都是过载多态的。如c语言中的
== : int * int -> int
== : double*double->int
== : char*char->int
有一些程序设计语言允许用户自定义过载多态的操作符。如语言中的:
bool operator == (Complex
bool operator ==(Address a1, Address a2);
过载多态的操作符或函数名,他所对应的通常是不同的实现。
2.4 强制多态
nbsp; 概念:编译程序通过语义操作,把操作对象的类型强行加以变换,以符合函数或操作符的要求。程序设计语言中基本类型的大多数操作符,在发生不同类型的数据进行混合运算时,
编译程序一般都会进行强制多态。程序员也可以显示地进行强制多态的操作(Casting)。
例如:
要注意的是并不是任意两个类型之间都可以进行强制多态。在不同类型之间实现强制多态,通常需要执行不同的转换操作。强制多态的原则是:将值集较小(即占用空间较小)的类型,变换成值集包含了前者(即占用存储空间较大)的类型,反之,应当注意可能发生的对值的损伤(特别是在使用Casting时)。
有时,强制多态与过载多态是混合出现的。例如,对于表达式1+2; 1.0+2; 1+2.0; 1.0+2.0;
中出现的多态,就会有多种解释:
·操作符+有四种过载多态;
·操作符+只有一种:double * double -> double, 要将参与运算的整数强制变换成浮点数;
·操作符+有两种过载多态:int * int -> int 和 double * double -> double,要将混合运算中的整数强制变换成浮点数。
三﹑ 其他类型的多态
现在的多数文献中都认为,在面向对象程序设计语言中还存在着两种多态:
·在有继承关系的类之间存在的多态(但不能完全等同于包含多态);
·通过动态绑定机制,在运行时才确定接受消息的对象类型(如c++语言中的虚拟函数)。
多态性是指用一个名字定义不同的函数,这函数执行不同但又类似的操作,从而实现“一个接口,多种方法”。
1.参数重载实现多态
send(int port),send(IPAddress ip,int port),多态性就通过函数重载实现了多态性,这两个函数名字相同,执行的也类似,只不过一个定义了一个目标端口,一个定义了目标IP地址和端口。
2. 通过虚函数来实现多态
Send(A *a);实参是父类还是子类的对象
虚函数是指在类中声明的,以后又可在由此类派生出来的一个或多个类中重新定义的函数(不久就会看到,不一定必须重新定义)
虚拟函数在中非常好玩的事。我们可以把需要改写的函数声明为虚函数,用virtual这个关键字来声明。
面向对象的语言最大的特色就是可以编写自己所需的数据类型,
|
类描述了一组有相同特性(属性)和相同行为(方法)的对象。在程序中,类实际上就是数据类型!例如:整数,小数等等。整数也有一组特性和行为。面向过程的语言与面相对象的语言的区别就在于,面向过程的语言不允许程序员自己定义数据类型,而只能使用程序中内置的数据类型!而为了模拟真实世界,为了更好的解决问题,往往我们需要创建解决问题所必需的数据类型!面向对象为我们提供了解决方案。
我们可以把函数想象成一个“实现某种特定功能的黑匣子”
return 关键字的含义是向调用者返回紧跟在它后面的信息
体的函数说明如下:
int myAge =0; //定义并初始化我的年龄为0;
int a=20; /*定义变量a等于20*/
void /*返回值类型为无返回值类型*/ remAge /*函数名称*/(int a /*传入的参数*/){
myAge=a; //内部实现方法,注意,没有return返回!!!
}
对象从类中产生出来!此时,对象具有类所描述的所有的属性以及方法。-------一定要理解这句话!!
!
为了实现数据的封装,提高数据的安全性,我们一般会把类的属性声明为私有的,而把类的方法声明为公共的。这样,对象能够直接调用类中定义的所有方法,当对象想要修改或得到自己的属性的时候就必须要调用以定义好的专用的方法才能够实现。我们提倡的是:“对象调方法,方法改属性”;
2.2通过实例看内存分配:
注意:请仔细观察对象是如何调用方法的,它使用了“.”操作符!事实上是这样的,对象调用公共的属性或方法时就会使用“.”操作符。
然而在中,如果定义一个同类型的指针,该指针调用此对象的方法时,就会使用“->”操作符
我想要和你说的是“声明”与“定义”之间的区别。声明只是告诉计算机将要有这样的一个变量(对象),在内存中它并不为这个变量(对象)分配内存!而只有在定义的时候才会给这个变量(对象)分配内存。
计算机只会为对象的属性分配内存。
2.3深入探讨函数:
构造函数是类中最特殊的函数,它与析构函数的功能正好相反!
从特征上来说:1.它是语言中唯一没有返回值类型的函数。
2.它的名称与类的名称必须要完全相同。
3.它必须被声明为公共(public)的类型
4,可以对构造函数进行重载。
5.它在创建对象是自动被调用。
从功能上来说:1.它是对类中的属性进行初始化。
1.2 内置数据类型与函数:
计算机程序在存储数据时必须跟踪3个基本属性为:
1. 信息存储在何处;
2. 存储的值是多少;
3. 存储的信息是什么类型的;
一、 函数参数传递机制的基本理论
函数参数传递机制问题在本质上是调用函数(过程)和被调用函数(过程)在调用发生时进行通信的方法问题。基本的参数传递机制有两种:值传递和引用传递。以下讨论称调用其他函数的函数为主调函数,被调用的函数为被调函数。
值传递(passl-by-value)过程中,被调函数的形式参数作为被调函数的局部变量处理,即在堆栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。
引用传递(pass-by-reference)过程中,被调函数的形式参数虽然也作为局部变量在堆栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过堆栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的
实参变量。