PB面向对象编程研究(三)

来源:互联网 发布:c语言字符串数组拷贝 编辑:程序博客网 时间:2024/06/06 01:50

4. 多态性

重载不是面向对象的专有技术。比如,全局的函数也有重载的概念。但是相比之下,重载在类以及类的继承中的应用要比全局函数的重载更加频繁和重要。

4.1. 重载

重载是实现多态的基础,多态由重载实现,但重载并不一定实现的都是多态。重载有两种,一种是子类对父类同名同参数函数的重载,即实现多态;第二种是,在同类或者子类中同名不同参数的函数重载,姑且称为类中的普通重载。

4.1.1. 重载实现多态

第一步,我们在uo_test类的基础上,继承uo_test类创建uo_test_inherited类,如果按照前面的步骤,该类应该已经存在;

第二步,我们打开uo_test_inherited类,选择Function中的func函数,如下面的图中红色标记;

 

第三步,添加如上图的代码。

至此,我们完成了对父类uo_testfunc方法在uo_test_inherited中的重载,下面将实现在程序中的调用,以演示func在类中的多态性。

在演示之前,为了明确究竟是调用的那一个类的func方法,我们将先前在uo_test中定义的func中的代码更改为下图内容:

 

下面开始正式演示PB中函数的多态性。

第一步,建立一个名为w_main_test的窗口,在窗口中添加函数polymorphism函数,如下图:

 

第二步,在w_main_testopen事件中添加如下代码,其中iu_testiu_test_inherited分别是uo_testuo_test_inherited的实例变量。如图:

 

创建对象后不要忘记销毁对象,本例中在w_main_testclose事件中进行的销毁,这里不作介绍。

第三步,运行程序,观察发生的情况:

 

 

我们发现,连续弹出两次“I am func of uo_test_inherited”的MessageBox窗口。

从运行结果我们可以知道,虽然定义的变量iu_test的类型是uo_test,但是创建的对象实体是uo_test_inherited类型,根据多态性,func应该调用的是uo_test_inherited类型中的func方法;在polymorphism函数中,虽然参数类型定义的是uo_test的类型,但是传入的参数实际上是uo_test_inherited类型,所以调用的依然是uo_test_inheritedfunc函数。

从上面的步骤中我们发现,我们仅仅是在uo_test_inherited类中重载了uo_test类中的func(integer ai_rtn)方法,我们并没有额外的做什么工作,不同于C++中,我们需要将成员函数声明为virtual类型。所以,在PB中每一个函数都可以在类的继承中重载实现多态,仅仅是重载。

4.1.2. 类中的普通重载

重载不仅仅是同名函数相同参数的实现多态方式的重载,也可以是同名函数不同参数的普通重载。也许,一直强调的“同名函数”似乎是多余的,因为,当函数名不同时也就无所谓重载了,不过,为了清晰强调一下也是必要的。

普通重载比实现多态的重载更加直接和简单。如下图,我们在uo_test_inherited类中增加一个func()的无参数版本。

 

同样,在w_main_test窗口的open事件中调用,看看实际结果如何,如下图:

 

我们发现,虽然iu_test的创建的实际类型是uo_test_inherited,但是,在调用func()时编译器却提示参数个数不对,这个进一步证明了面向对象中的多态性,无论创建或者传入的参数是什么类型,对象的类型依然依据声明时的类型,但是传入的类型可以影响被重载函数(在C++中被称为虚拟函数)的调用。

我们去掉错误的代码,运行,然后观察结果:

 

func()无参数版被正确调用了。函数名被成功的重载了,利用不同的参数,我们可以实现不同的处理了。

4.2. 动态调用

动态调用是PB区别于其他面向对象语言的一个特性,例如C++。动态调用与多态类似,或者我们可以称其为增强的多态性。动态调用究竟是什么,下面便研究便揭开谜底。

4.1.2节中,使用iu_test调用func()无参数版本时发生了编译错误,因为uo_test类型中没有func()的无参数版本,但是事实上,iu_test的实例是通过uo_test_inherited类而创建的,在这个类中确实存在这个func()的无参数版本。如果实现多态性必须在每一个类中都要声明一个func()函数,而有时在某些父类中该函数不一定必须存在,而又需要该父类作为一个载体调用该函数该如何解决呢?面对这个问题,我们针对动态调用做个试验。

第一步,在w_main_test窗口中将iu_test.func()的代码更改为iu_test.dynamic func(),保存,看看编译器的反应,如下图:

 

编译器接受了,虽然uo_test类中不存在这个函数。

第二步,运行程序,看看实际结果如何:

 

 

连续出现了两个func()无参数版本的Messagebox窗口,这说明,通过Dynamic关键字实现了不需要每一个类都定义同名、同参数的函数来实现函数的多态性!这是PB对多态性的一个增强。

但是我更愿意说,这是对PB编译器的一个恶意欺骗。我们可以想象一下,如果写出这样的代码会怎样:iu_test.dynamic abc()?答案是什么,试试就知道!

PB中,所有的对象均继承于powerobject对象,那么,我们下面修改一下w_main_test窗口的polymorphism函数的参数试一下,看看有什么结果。修改后的函数如下图:

 

此时,au_test已经是PowerObject对象实例了,本身已经不具备func(integer ai_rtn)有参数版本的函数了,但是编译器依然接受这样的代码,因为我们使用了动态调用关键字Dynamic关键字。

运行程序,我们发现与先前的测试没什么两样,依然是连续两次弹出如下图的MessageBox窗口:

 

 

说明我们的修改并没有对程序的运行结果造成什么影响。

动态调用给我们提供了更灵活的工具,我们可以在我们自己编写的类中加入同样的函数,而这个函数对于不同的类却有不同的操作,使用动态调用的功能,实现更普遍的代码。

有利也有弊,由于使用Dynamic关键字,使编译器不再检查该调用的有效性,或者说,编译器根本无法检查该调用在运行时是否正确,就像上面的例子中调用abc()函数一样。造成的结果便是系统的非法中断,而抛出系统异常。

 

 

5. 总结

本文的知识点不是甚多,总结如下:

1、 属性,类中的数据,类的特性,实现数据的封装与隐藏

2、 方法,类的成员函数,类的行为,实现对属性的操作

3、 属性与方法都具有访问权限

4、 对于方法的参数,选择合适的传递类型

5、 在构造事件中实现属性的初始化和资源的申请

6、 在析构事件中实现资源的释放,如:destroy iu_test

7、 继承类的构造与析构顺序都是先父后子

8、 类的继承性与类的多态性相互关联,继承性是多态性的基础

9、 父类的每一个函数都可以被子类重载,在调用时均可以实现多态调用

10、 没有在父类定义的函数,可以使用Dynamic关键字实现多态调用

11、 动态调用要求程序员必须清楚运行时的每一种可能

 

0 0