隐藏派生类型的数据成员和自定义算符矛盾了怎么办?
例如定义一个派生类型是三维矢量TYPE :: vect
Real(8) ,Private :: x
Real(8) ,Private :: y
Real(8) ,Private :: z
End TYPE
然后希望定义矢量的数乘,并且希望可以用“*”号进行运算,
由于涉及的数据成员都是隐藏的,所以只能用绑定的方法实现
例如在派生类型定义改成:
TYPE :: Vect
Real(8) ,Private :: x
Real(8) ,Private :: y
Real(8) ,Private :: z
Contains
Generic, Public :: operator (*) => ScalMul
Proceduce,Private :: ScalMul => VectMulScal_Fn, ScalMulVect_Fn
End TYPE
Private :: VectMulScal_Fn, ScalMulVect_Fn
然后再在模块的内部子程序列表里写出标量右乘矢量和标量左乘矢量的这两个函数实现
可是如果自定义算符要绑定类型的话,其实现方法必须是 PASS 类型的
也就导致二元算符“*”的左边必须是三元矢量的派生类型
像我刚才写的这段代码里,ScalMulVect_Fn(Scal,ThisVect)实际上根本就不被允许
可是实际矢量数乘运算时,很可能是实数在左,符号在右
所以实际上想实现的功能根本无法实现
另外,自定义算符也可以不绑定派生类型Vect
但是这样会导致其实现函数不能直接访问派生类型Vect的数据成员坐标吧?
这种情况怎么解决好呢?
我想到有两种解决办法,还请各位大神指教,也请大神有更好的办法教教我
第一种办法是不用绑定类型Vect,派生类型Vect的数据成员不设成Private,而是Protected
这样的坏处是数据成员并没有完全隐藏吧?似乎外界还是可以用Vect1%x,Vect2%y这种方法直接读数据
只是不能更改他们的值而已?这样感觉隐藏效果不佳意图没有完全实现
第二种办法也还还没办法绑定类型Vect,派生类型Vect的数据成员可以设成为Private
但是算符的实现函数ScalMulVect_Fn和VectMulScal_Fn中就不能直接引用Vect类型参量的数据成员了
只能用绑定的Get函数来读数据和用绑定的Set子程序来赋值
这个办法开起来似乎意图全都实现了,但是感觉平白多了好多函数引用,是不是会影响效率啊…… SO上面的帖子,或许有所帮助
https://stackoverflow.com/questions/19837888/protected-inheritance-in-fortran-2003-2008
https://stackoverflow.com/questions/25410476/understanding-fortran-extends-types-and-override
https://softwareengineering.stackexchange.com/questions/303245/private-variables-and-the-old-fortran-common-blocks
另一个思路就是参考C如何模拟面向对象,譬如GTK、GIMP这类开源框架
lz身处工业界的话,个人建议:C++改写或者混合编程,Eigen、armadillo这类线性代数模板库上手很容易 Module VectClass
TYPE :: Vect
Real(8) ,Private :: x
Real(8) ,Private :: y
Real(8) ,Private :: z
Contains
Procedure :: set
Procedure,pass(ThisVect),Private :: VectMulScal_Fn , ScalMulVect_Fn , VectMulVect_Fn
Generic, Public :: operator (*) => VectMulScal_Fn , ScalMulVect_Fn , VectMulVect_Fn
Procedure :: writef
Generic :: write(formatted) => writef
End TYPE
contains
Subroutine set(this,x,y,z)
class(Vect) :: this
Real(8), Intent(IN) :: x , y , z
this%x = x ; this%y = y ; this%z = z
End Subroutine set
Subroutine writef(this, unit, iotype, v_list, iostat, iomsg)
class(vect), intent(in) :: this
integer, intent(in) :: unit
character (len=*), intent(in) :: iotype
integer, intent(in) :: v_list(:)
integer, intent(out) :: iostat
character (len=*), intent(inout) :: iomsg
write(unit , iotype(3:) , iostat=iostat, iomsg=iomsg) this%x , this%y , this%z
End Subroutine writef
Type(Vect) Function ScalMulVect_Fn(ThisVect,Scal) result( y )
real(8) , Intent(IN) :: Scal
class(Vect) , Intent(IN) :: ThisVect
y = Vect( ThisVect%x * Scal , ThisVect%y * Scal , ThisVect%z * Scal )
End Function ScalMulVect_Fn
Type(Vect) Function VectMulScal_Fn(Scal,ThisVect) result( y )
real(8) , Intent(IN) :: Scal
class(Vect) , Intent(IN) :: ThisVect
y = ThisVect * Scal
End Function VectMulScal_Fn
Type(Vect) Function VectMulVect_Fn(ovect,ThisVect) result( y )
class(Vect) , Intent(IN) :: ovect , ThisVect
y = Vect( ThisVect%x * ovect%x , ThisVect%y * ovect%y , ThisVect%z * ovect%z )
End Function VectMulVect_Fn
End Module VectClass
Program Main
use VectClass
type(Vect) :: a , b , c
call a%set(1.d0,2.d0,3.d0)
b = a * 3.0d0
write(*,'(a,dt"(3f6.2)")') "b=" , 3.0d0*b
c = a * ( 3.d0 * b )
write(*,'(dt"(3f6.2)")') c
End Program Main
楚香饭 发表于 2018-11-5 23:12
Module VectClass
TYPE :: Vect
Real(8) ,Private :: x
非常感谢版主,原来可以在声明的Pass处直接用关键字申明自动传送的派生类型参量名
这样就可以改变传送的位置了,类似于Optional参量的做法
非常感谢!
版主总是一言不发,用代码直接说明问题,让人茅塞顿开,太6了! pasuka 发表于 2018-11-5 21:32
SO上面的帖子,或许有所帮助
https://stackoverflow.com/questions/19837888/protected-inheritance-in-for ...
非常感谢您的帮助
这里有些帖子确实和我有些像,也有些不同
看来Fortran做oop还是有很多问题啊
只要隐藏数据,就会带来各种各样的问题啊
而且没有真正的构造函数,看起来确实真正想做oop确实应该用c之类的
可惜我确实不是工业界的啊,而是希望用Fortran做大量科学计算的科学民工
C++的oop有时间还会再研究,现在先把Fortran用熟吧…… 谈不上帮忙
能够提出这类层级的问题,首先祝贺lz的Fortran水平已经跻身大中华区前5%,坏消息是后面还有OOP方面的深层次问题,恐怕直接去intel的Fortran论坛、GCC的邮件列表或者SO效果更好
页:
[1]