14

目前是否可以覆盖 Fortran 中的结构构造函数?我见过这样的建议示例(例如在 Fortran 2003 规范中):

module mymod

  type mytype
    integer :: x
    ! Other stuff
  end type

  interface mytype
    module procedure init_mytype
  end interface

contains
  type(mytype) function init_mytype(i)
    integer, intent(in) :: i
    if(i > 0) then
      init_mytype%x = 1
    else
      init_mytype%x = 2
    end if
  end function
end

program test
  use mymod
  type(mytype) :: x
  x = mytype(0)
end program

由于冗余变量名称,这基本上会产生一堆错误(例如,错误:'mytype' 的 DERIVED 属性与 (1) 处的 PROCEDURE 属性冲突)。fortran 2003 示例的逐字副本会产生类似的错误。我已经在 gfortran 4.4、ifort 10.1 和 11.1 中尝试过,它们都会产生相同的错误。

我的问题:这只是 fortran 2003 的一个未实现的功能吗?还是我执行不正确?

编辑:我遇到了一个错误报告和一个关于这个问题的 gfortran补丁。但是,我尝试使用 gcc46 的 11 月版本,但没有运气和类似的错误。

编辑 2:上面的代码似乎可以使用 Intel Fortran 12.1.0。

4

2 回答 2

19

目前是否可以覆盖 Fortran 中的结构构造函数?

不。无论如何,即使使用您的方法也完全不是构造函数覆盖。主要原因是结构构造函数#OOP 构造函数。有一些相似之处,但这只是另一个想法。

您不能在初始化表达式中使用非内在函数。您只能使用常量、数组或结构构造函数、内部函数……有关更多信息,请查看 Fortran 2003 草案中的 7.1.7 初始化表达式。

考虑到这一事实,我完全不明白两者之间的真正区别是什么

type(mytype) :: x
x = mytype(0)

type(mytype) :: x
x = init_mytype(0)

在 mymod MODULE 中使用 INTERFACE 块的重点是什么。

好吧,老实说,这是有区别的,很大的区别-第一种方式具有误导性。这个函数不是构造函数(因为在 Fortran 中根本没有 OOP 构造函数),它是一个初始化器。


在主流的 OOP 中,构造函数负责依次做两件事:

  1. 内存分配。
  2. 成员初始化。

让我们看一些用不同语言实例化类的例子。

Java中:

MyType mt = new MyType(1);

隐藏了一个非常重要的事实 - 对象实际上是指向类类型变量的指针。C++中的等价物将使用以下方法在堆上分配

MyType* mt = new MyType(1);

但是在这两种语言中,我们都可以看到,即使在语法级别上也反映了两种构造函数的职责。它由两部分组成:关键字new(分配)和构造函数名称(初始化)。在Objective-C语法中,这一事实更加强调:

MyType* mt = [[MyType alloc] init:1];

但是,很多时候,您可以看到其他形式的构造函数调用。在堆栈上分配的情况下,C++使用特殊的(非常差的)语法构造

MyType mt(1);

这实际上具有误导性,以至于我们不能考虑它。

Python中

mt = MyType(1)

对象实际上是一个指针的事实和首先发生分配的事实都是隐藏的(在语法级别)。而这种方法叫做…… __init__!O_O 太误导了。С++ 堆栈分配与那个相比消失了。=)


无论如何,在语言中使用构造函数的想法意味着能够使用某种特殊的方法在一个语句中进行分配初始化。如果您认为这是“真正的 OOP”方式,我有坏消息要告诉您。甚至Smalltalk 也没有构造函数。在类本身上有一个方法只是一个约定new(它们是元类的单例对象)。工厂设计模式用于许多其他语言以实现相同的目标。

我在某处读到 Fortran 中的模块概念受到 Modula-2 的启发。在我看来,OOP 功能似乎受到Oberon-2的启发。Oberon-2 中也没有构造函数。但是当然有带有预声明过程 NEW 的纯分配(就像 Fortran 中的 ALLOCATE,但 ALLOCATE 是语句)。分配后,您可以(实际上应该)调用一些初始化程序,这只是一个普通方法。那里没什么特别的。

所以你可以使用某种工厂来初始化对象。这是您实际上使用模块而不是单例对象所做的。或者最好说他们(Java/C#/...程序员)使用单例对象方法而不是普通函数,因为缺少后者(没有模块 - 没有普通函数,只有方法)。

您也可以改用类型绑定的 SUBROUTINE。

MODULE mymod

  TYPE mytype
    PRIVATE
    INTEGER :: x
    CONTAINS
    PROCEDURE, PASS :: init
  END TYPE

CONTAINS

  SUBROUTINE init(this, i)
    CLASS(mytype), INTENT(OUT) :: this
    INTEGER, INTENT(IN) :: i

    IF(i > 0) THEN
      this%x = 1
    ELSE
      this%x = 2
    END IF
  END SUBROUTINE init

END

PROGRAM test

  USE mymod

  TYPE(mytype) :: x

  CALL x%init(1)

END PROGRAM

INTENT(OUT)对于SUBROUTINE 的thisarginit似乎没问题。因为我们希望这个方法在分配之后只被调用一次。控制这个假设不会是错误的可能是一个好主意。要向 中添加一些布尔标志LOGICAL :: initedmytype请检查它是否存在.false.,并在第一次初始化时将其设置为.true.,并在尝试重新初始化时执行其他操作。我绝对记得在 Google Groups 中有一些关于它的主题......我找不到它。

于 2010-11-25T20:13:20.917 回答
6

我查阅了我的 Fortran 2008 标准副本。这确实允许您定义与派生类型同名的泛型接口。我的编译器(英特尔 Fortran 11.1)不会编译代码,所以我怀疑(手头没有 2003 标准的副本)这是 Fortran 2003 标准的一个尚未实现的功能。

除此之外,您的程序中存在错误。你的函数声明:

  type(mytype) function init_mytype
    integer, intent(in) :: i

指定函数规范中不存在的参数的存在和意图,可能应该重写为:

  type(mytype) function init_mytype(i)
于 2010-11-24T07:58:09.433 回答