Consider one of the classic OOP examples (see source code at the end of the post):
- Abstract base class Shape
- Class Rectangle extending Shape
Questions:
- In the source code below I've tried to define a constructor for the abstract class Shape using
class(Shape), pointer :: this
as result without ever allocating the pointer. Is this the correct way of defining a constructor for an abstract class in Fortran? - How can I invoke the constructor of the base class (Shape) in the constructor of the extending class (Rectangle)?
Example Source Code
Updated with suggestion from Ed Smith which works for non-abstract base classes.
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
This program gives the following output:
Constructing rectangle
Rectangle with center point 6.9194863361077724E-310 6.9194863361077724E-310 has area 12.000000000000000
Since the base class constructor haven't been invoked the centerPoint variable isn't initalized. In this simple example the variable could be initialized manually from the Rectangle constructor, but for more complex cases this might lead to significant duplication of code.