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

标签 oop fortran

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

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

  • 问题:
  • 在下面的源代码中,我尝试使用 class(Shape), pointer :: this 为抽象类 Shape 定义构造函数。结果没有分配指针。这是在 Fortran 中为抽象类定义构造函数的正确方法吗?
  • 如何在扩展类(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
    

    该程序给出以下输出:
     Constructing rectangle
     Rectangle with center point   6.9194863361077724E-310   6.9194863361077724E-310  has area    12.000000000000000 
    

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

    最佳答案

    这是一个很好的问题,希望有更多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
    

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

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

    如果没有,作为您想要的最小工作解决方案,我最终使用的解决方案是有一个定义接口(interface)的抽象形状类,然后在继承它的第一个对象中定义构造函数,这里是 shape类型(类似于 this 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
    

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

    关于oop - 在 Fortran 中调用抽象基类的构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30823756/

    相关文章:

    javascript - JS : create a subclass instance from a superclass instance

    oop - 根据数学群论设计对象好不好

    java - OO PHP 服务性能

    binary - 带有二进制数据的 gnuplot 中的 pm3d

    c++ - 如何以及何时执行静态链接 (MinGW)?

    vim - Fortran 77 注释的语法高亮显示在 vim 中不起作用

    fortran - 四倍精度与 double 的 CPU 时间

    c++ - 如何在两个类中双重引用子类和父类

    Perl - 从父类(super class)(OO)调用子类构造函数

    makefile - 使用 CMake 生成的 makefile 的并行作业(没有速度改进)