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

时间:2015-06-13 21:34:01

标签: oop fortran

考虑一个经典的OOP示例(请参阅帖子末尾的源代码):

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

问题:

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

    更新了来自Ross Ridge的建议,该建议适用于非抽象基类。

    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
    

    该程序提供以下输出:

     Constructing rectangle
     Rectangle with center point   6.9194863361077724E-310   6.9194863361077724E-310  has area    12.000000000000000 
    

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

2 个答案:

答案 0 :(得分:2)

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

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

对于第二个问题,答案应该是在矩形构造函数中使用行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

我无法使用intel编译器v13.0.1。它返回错误:If the rightmost part-name is of abstract type, data-ref shall be polymorphic。据我所知,fortran 2008标准应该允许你调用抽象类型的构造函数,如果它是当前类型的父类。这可能适用于以后的编译器,请查看this answer(并尝试您的情况)。

如果没有,作为你想要的最小工作解决方案,我最终使用的解决方案是拥有一个定义接口的抽象形状类,然后在第一个继承了这个的对象中定义构造函数,这里是{{ 1}}类型(类似于this fortran oop示例的第11.3.2节)。解决方案如下,

shape

很抱歉,这个符号与你的例子略有不同,但希望这会有所帮助......

答案 1 :(得分:1)

"构造函数"您正在寻找的概念最好通过"初始化"一个子程序,它接受一个I​​NTENT([IN] OUT)多态参数和抽象父类的声明类型,如Ed Smith的答案的第二部分所示。

作为概念背景 - 您不能在Fortran中创建具有抽象类型的值(这会破坏ABSTRACT的含义),但这正是您尝试使用父项的构造函数。

(这里有一个不同之处在于创建一个值,然后将该值存储在其他一些对象中。非抽象类型的值可能存储在一个多态对象中,该对象具有一个抽象的声明类型,并且是一个值类型的父类型。)