2.面向对象的Fortran 从结构化程序设计到面向对象,这是大多数高级语言的发展、演变规律,Fortran也未能免 俗。在Fortran 77中,由于缺乏对数据抽象和数据封装的基本支持,人们很难引入面向对 象的理念和方法。在Fortran 90/95中,尽管我们可以用派生类型(Derived Type)和模块 (Module)模拟一部分面向对象的特性,但却无法实现真正意义上的封装和继承。 对数据和相关操作的封装是面向对象的基础。传统的结构化程序设计语言大多拥有简单的 数据封装能力(如Pascal语言中的record),但缺乏对操作封装的支持。当我们为结构化 程序设计语言引入面向对象机制时,在语法层面一般有两种思路:一种是引入一个全新的 语法容器,允许用户借助该容器定义出封装了数据和操作的统一数据类型,例如,C++在C 语言的基础上扩展出了class的概念,并以class为中心支持面向对象编程;另一种是通过 某种机制,将原有的数据封装容器和表述操作的过程或函数捏合在一起,例如,Ada 95利 用Ada 83中已有的概念,将private type和相关操作封装在package中,创建了完整的面向 对象机制。Fortran 2003对Fortran 90/95的改造正是遵循着后一种思路进行的。 首先,Fortran 2003第一次引入了过程指针(Procedure Pointer)的概念。Fortran 90/ 95只支持普通指针。为了实现回调机制,Fortran 90/95特意引入了用过程名充当形式参数 (Dummy Procedure)的语法,但这种语法还无法解决在数据类型中内嵌相关操作的问题。 在Fortran 2003中,我们可以使用过程指针将数据和操作绑定在一起: TYPE point REAL :: x, y PROCEDURE (testif), POINTER :: test END TYPE 这种绑定是一种运行时的动态绑定,我们可以根据需要为过程指针赋值,这类似于我们在 C语言的struct中嵌入函数指针。 其次,如果与类型相关的操作在编译时已经预知,我们就可以直接使用Fortran 2003提供 的类型绑定过程(Type-bound Procedure),将数据和操作关联在一起: TYPE point REAL :: x, y CONTAINS PROCEDURE :: test => my_test END TYPE 这样,在同一个模块中,类型和相关操作相互关联,用户可以直接使用CALL p1%test或类 似的代码来访问特定操作,这已经非常近似于Ada 95的封装机制了。 和C++或Java语言相比,Fortran 2003的这种过程绑定方式较为靠近语言设计的底层。例如 ,对于绑定在point类型中的my_test过程,我们必须显示指明,该过程的第一个参数为po int类型: SUBROUTINE my_test(a) TYPE(point), INTENT(IN) :: a END SUBROUTINE 这里的参数a相当于C++或Java方法中隐含的this参数,其目的是将调用该操作的对象实例 传入过程。我把这种显示实现this参数的做法称为“赤裸裸的面向对象”。尽管写起来稍 嫌繁琐,但这种“赤裸裸”的做法也有不少好处。例如,我们可以在Fortran 2003中使用 NOPASS属性禁止传递对象实例,或通过PASS属性改变对象实例参数在过程参数表中的位置 。 …… …… ……
3.自由的Fortran 灵活性一直是Fortran语言的一个弱项。早期的Fortran甚至不支持内存的动态分配。Fort ran 90引入了指针的概念,全面支持数据的动态存取,同时也引入了泛型过程(Generic Procedure)的机制,以支持与C++的函数重载类似的功能。在此基础上,Fortran 2003通 过参数化派生类型、覆盖、多态型等机制,进一步增强了Fortran语言的灵活性。例如:
TYPE point(k, n) INTEGER, KIND :: k = KIND(0.0D0) INTEGER, LEN :: n REAL(KIND=k) :: x, y CHARACTER(LEN=n) :: name END TYPE 基于上面这样的参数化派生类型(Parameterized Derived Type),我们可以在定义对象 时,根据需要指定对象的具体类型参数,也可以在程序运行期间,动态指定可分配内存对 象的类型参数(Deferred Type Parameter)。 除了Fortran 90已经实现的重载(Overloading)机制以外,Fortran 2003还允许扩展类型 覆盖(Overriding)基类型中的类型绑定过程。更重要的是,Fortran 2003通过抽象类型 (Abstract Type)、延迟绑定(Deferred Binding)、多态实体(Polymorphic Entity) 、类型选择结构(SELECT TYPE)等要素完整地实现了面向对象语言必备的多态机制。举例 来说,我们可以声明下面这样的抽象类型: TYPE, ABSTRACT :: point REAL :: x, y CONTAINS PROCEDURE(testif), DEFERRED :: test END TYPE ABSTRACT INTERFACE SUBROUTINE testif(p) CLASS(point), INTENT(in) :: p END SUBROUTINE END INTERFACE 上述代码相当于在C++或Java语言中声明了一个抽象类,其中的test过程相当于C++语言中 的一个纯虚函数,只有接口声明,而没有实现代码。在此基础上,我们可以从抽象类型po int扩展出具体的子类型。 当某个对象的具体类型在运行时才能确定时,我们可以用CLASS关键字(这里的CLASS指的 是一组有继承关系的类型,与C++中的class并不等同)定义一个具有多态功能的指针或可 分配对象: CLASS(point), POINTER :: p 现在,指针p可以指向任何由point扩展的具体类型的对象,这种使用方式已经和我们在C+ +中用基类指针引用派生类对象的做法一模一样了。使用这种具有多态性特征的实体时,我 们还可以用Fortran 2003提供的内部过程SAME_TYPE_AS和EXTENDS_TYPE_OF来判定对象的类 型,这是一种简单的运行时类型识别(RTTI)机制,其功能和C++语言中的typeid关键字相 仿。 …… …… ……