在 Fortran 中调用抽象基类的构造函数

Pau*_*aul 6 oop fortran

考虑经典的 OOP 示例之一(请参阅文章末尾的源代码):

  • 抽象基类Shape
  • 类 Rectangle 扩展形状

问题:

  1. 在下面的源代码中,我尝试使用class(Shape), pointer :: thisas 结果为抽象类 Shape 定义一个构造函数,而无需分配指针。这是在 Fortran 中为抽象类定义构造函数的正确方法吗?
  2. 如何在扩展类(Rectangle)的构造函数中调用基类(Shape)的构造函数?

示例源代码

更新了Ed Smith 的建议,该建议适用于非抽象基类。

module Shape_mod
    implicit none

    private
    public Shape

    type, abstract :: Shape
        private
        double precision :: centerPoint(2)
    contains
        procedure :: getCenterPoint
        procedure(getArea), deferred :: getArea
    end type Shape

    interface Shape
        module procedure constructor
    end interface Shape

    abstract interface 
        function getArea(this) result(area)
            import
            class(Shape), intent(in) :: this
            double precision :: area
        end function getArea
    end interface 

contains

    !Correct way of defining a constructor for an abstract class?
    function constructor(xCenter, yCenter) result(this)   
        class(Shape), pointer :: this
        double precision, intent(in) :: xCenter
        double precision, intent(in) :: yCenter

        print *, "constructing base shape"
        this%centerPoint = [xCenter, yCenter]
    end function constructor

    function getCenterPoint(this) result(point)
        class(Shape), intent(in) :: this
        double precision point(2)
        point = this%centerPoint
    end function getCenterPoint

end module Shape_mod

module Rectangle_mod
    use Shape_mod
    implicit none

    private
    public Rectangle

    type, extends(Shape) :: Rectangle
        private
        double precision :: length
        double precision :: width
    contains
        procedure :: getArea
    end type Rectangle


    interface Rectangle
        module procedure constructor
    end interface Rectangle

contains

    function constructor(length, width, xCenter, yCenter) result(this)
        type(Rectangle), pointer :: this
        double precision :: length
        double precision :: width
        double precision :: xCenter
        double precision :: yCenter

        print *, "Constructing rectangle"

        allocate(this)
        this%length = length
        this%width = width
        !How to invoke the base class constructor here?
        !The line below works for non-abstract base classes where the 
        !constructor result can be type(Shape)
        this%Shape = Shape(xCenter, yCenter) 
    end function constructor

    function getArea(this) result(area)
        class(Rectangle), intent(in) :: this
        double precision :: area

        area = this%length * this%width
    end function getArea

end module Rectangle_mod

program main
    use Rectangle_mod
    implicit none
    type(Rectangle) :: r

    r = Rectangle(4.0d0, 3.0d0, 0.0d0, 2.0d0)
    print *, "Rectangle with center point", r%getCenterPoint(), " has area ", r%getArea()
end program main
Run Code Online (Sandbox Code Playgroud)

该程序提供以下输出:

 Constructing rectangle
 Rectangle with center point   6.9194863361077724E-310   6.9194863361077724E-310  has area    12.000000000000000 
Run Code Online (Sandbox Code Playgroud)

由于尚未调用基类构造函数,因此未初始化 centerPoint 变量。在这个简单的例子中,可以从 Rectangle 构造函数手动初始化变量,但对于更复杂的情况,这可能会导致大量的代码重复。

Ed *_*ith 5

这是一个很好的问题,我希望有更多 fortran oop 经验的人可以给出更好的答案。对于第一个问题,您不需要指针,而是可以将构造函数定义为,

type(Shape) function constructor(xCenter, yCenter)   

    double precision, intent(in) :: xCenter
    double precision, intent(in) :: yCenter

    print *, "constructing base shape"
    constructor%centerPoint = [xCenter, yCenter]
end function constructor
Run Code Online (Sandbox Code Playgroud)

对于第二个问题,答案应该是使用矩形构造函数中的行调用constructor%Shape = Shape(xCenter, yCenter)矩形构造函数中的父级。

type(Rectangle) function constructor(length, width, xCenter, yCenter)

    type(Rectangle), pointer :: this
    double precision, intent(in) :: xCenter
    double precision, intent(in) :: yCenter
    double precision, intent(in) :: length
    double precision, intent(in) :: width

    print *, "Constructing rectangle"

    !invoke the base class constructor here
    constructor%Shape_ = Shape(xCenter, yCenter)
    constructor%length = length
    constructor%width = width

end function constructor
Run Code Online (Sandbox Code Playgroud)

我无法让它与英特尔编译器 v13.0.1 一起使用。它返回错误:If the rightmost part-name is of abstract type, data-ref shall be polymorphic。据我了解,fortran 2008 标准应该允许您调用抽象类型的构造函数(如果它是当前类型的父类型)。这可能在以后的编译器中有效,请查看此答案(并尝试针对您的情况)。

如果没有,作为您想要的最小工作解决方案,我最终使用的解决方案是有一个定义接口的抽象形状类,然后在继承它的第一个对象中定义构造函数,这里是一个shape类型(类似于部分Fortran oop 示例的 11.3.2 )。解决方案如下,

module shape_mod

    type, abstract :: abstractshape
            integer :: color
            logical :: filled
            integer :: x
            integer :: y
    end type abstractshape

   interface abstractshape
        module procedure initShape
    end interface abstractshape

    type, EXTENDS (abstractshape) :: shape
    end type shape

    type, EXTENDS (shape) :: rectangle
            integer :: length
            integer :: width
    end type rectangle

    interface rectangle
        module procedure initRectangle
    end interface rectangle


contains

    ! initialize shape objects
    subroutine initShape(this, color, filled, x, y)

        class(shape) :: this
        integer :: color
        logical :: filled
        integer :: x
        integer :: y

        this%color = color
        this%filled = filled
        this%x = x
        this%y = y

    end subroutine initShape

    ! initialize rectangle objects
    subroutine initRectangle(this, color, filled, x, y, length, width)

        class(rectangle) :: this
        integer :: color
        logical :: filled
        integer :: x
        integer :: y
        integer, optional :: length  
        integer, optional :: width   

        this%shape = shape(color, filled, x, y)

        if (present(length)) then
           this%length = length
        else
           this%length = 0
        endif
        if (present(width)) then 
            this%width = width
        else
             this%width = 0
        endif
    end subroutine initRectangle

end module shape_mod

program test_oop
    use shape_mod 
    implicit none

    ! declare an instance of rectangle
    type(rectangle) :: rect 

    ! calls initRectangle 
    rect = rectangle(2, .false., 100, 200, 11, 22)  

    print*, rect%color, rect%filled, rect%x, rect%y, rect%length, rect%width 

end program test_oop
Run Code Online (Sandbox Code Playgroud)

抱歉,符号与您的示例略有不同,但希望这会有所帮助......