考虑一个经典的 OOP 示例(参见文章末尾的源代码):
问题:
class(Shape), pointer :: this
为抽象类 Shape 定义构造函数。结果没有分配指针。这是在 Fortran 中为抽象类定义构造函数的正确方法吗? 示例源代码
更新了来自 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/