Как переопределить конструктор структуры в fortran
возможно ли в настоящее время переопределить конструктор структуры в Fortran? Я видел такие примеры (например, в спецификации Fortran 2003):
module mymod
type mytype
integer :: x
! Other stuff
end type
interface mytype
module procedure init_mytype
end interface
contains
type(mytype) function init_mytype(i)
integer, intent(in) :: i
if(i > 0) then
init_mytype%x = 1
else
init_mytype%x = 2
end if
end function
end
program test
use mymod
type(mytype) :: x
x = mytype(0)
end program
это в основном генерирует кучу ошибок из-за избыточных имен переменных (например, ошибка: производный атрибут "mytype" конфликтует с атрибутом процедуры в (1)). Стенографическая копия примера fortran 2003 создает аналогичные ошибки. Я пробовал это в gfortran 4.4, ifort 10.1 и 11.1, и все они производят те же ошибки.
мой вопрос: это просто нереализованная функция fortran 2003? Или я неправильно это реализую?
Edit: я наткнулся на сообщить об ошибке и анонсирован патч gfortran по этому вопросу. Тем не менее, я попытался использовать ноябрьскую сборку gcc46 без везения и подобных ошибок.
Edit 2: приведенный выше код работает с использованием Intel Fortran 12.1.0.
2 ответов
я сверился со своей копией стандарта Fortran 2008. Что позволяет определить универсальный интерфейс с тем же именем в производном типе. Мой компилятор (Intel Fortran 11.1) не будет компилировать код, хотя я подозреваю (без копии стандарта 2003), что это еще не реализованная функция стандарта Fortran 2003.
кроме того, в вашей программе есть ошибка. Ваше объявление функции:
type(mytype) function init_mytype
integer, intent(in) :: i
указывает существование и намерение аргумента, которого нет в спецификации функции, которое, возможно, следует переписать как:
type(mytype) function init_mytype(i)
возможно ли в настоящее время переопределить конструктор структуры в Fortran?
нет. в любом случае даже использование вашего подхода полностью не связано с переопределением конструктора. Основная причина заключается в том, что конструктор структуры # OOP конструктор. Есть некоторое сходство, но это просто другая идея.
вы не можете использовать свою не-внутреннюю функцию в выражении инициализации. Вы можете использовать только константу, массив или структуру конструктор, встроенные функции, ... Для получения дополнительной информации взгляните на выражение инициализации 7.1.7 в Fortran 2003 draft.
принимая во внимание этот факт, я полностью не понимаю, в чем реальная разница между
type(mytype) :: x
x = mytype(0)
и
type(mytype) :: x
x = init_mytype(0)
и в чем весь смысл использования интерфейсного блока внутри модуля mymod.
ну, честно говоря, есть разница, огромная - первый способ вводит в заблуждение. Этот функция не является конструктором (поскольку в Fortran вообще нет конструкторов ООП), она является инициализатором.
в основном конструкторе ООП отвечает за последовательное выполнение двух вещей:
- выделение памяти.
- инициализации членов.
давайте рассмотрим некоторые примеры создания экземпляров классов на разных языках.
In Java:
MyType mt = new MyType(1);
очень важный факт скрыт-тот факт, что объект на самом деле является указателем на varibale типа класса. Эквивалент в C++ будет распределение по куче использование:
MyType* mt = new MyType(1);
но на обоих языках можно видеть, что две обязанности конструктора отражены даже на уровне синтаксиса. Он состоит из двух частей: ключевое слово new (выделение) и имя конструктора (инициализация). В С синтаксис этот факт еще более подчеркивал:
MyType* mt = [[MyType alloc] init:1];
много раз, однако, вы можете увидеть некоторую другую форму вызова конструктора. В случае распределение по стеку C++ использует специальную (очень плохую) синтаксическую конструкцию
MyType mt(1);
что на самом деле настолько вводит в заблуждение, что мы можем просто не рассматривать его.
на Python
mt = MyType(1)
как на самом деле объект на самом деле является указателем и то, что выделение происходит первым, скрыто (на уровне синтаксиса). И этот метод называется ... __init__
! O_O так вводит в заблуждение. Выделение стека С++ блекнет в сравнении с этим. =)
в любом случае, идея конструктор в языке подразумевают способность делать выделение инициализации в одном операторе используя какой-то специальный метод. И если вы думаете, что это" истинный ОП", у меня для вас плохие новости. Даже Smalltalk не имеет конструкторов. Это просто конвенция, чтобы иметь new
метод на самих классах (они являются одноэлементными объектами мета-классов). The Шаблон Проектирования Фабрики используется во многих других языках для достижения той же цели.
я где-то читал, что концепции модулей в Fortran были вдохновлены Modula-2. И мне кажется, что функции ООП вдохновлены Оберон-2. Нет конструкторов в Оберон-2 также. Но есть, конечно, чистое распределение с предшествующей процедурой NEW (например, ALLOCATE в Fortran, но ALLOCATE is statement). После выделения вы можете (должны на практике) вызвать некоторый инициализатор, который является обычным методом. Ничего особенного.
таким образом, вы можете использовать какие-то фабрики для инициализации объектов. Это то, что вы на самом деле сделали, используя модули вместо одноэлементных объектов. Или лучше сказать, что они (Java/C#/... программисты) используют одноэлементные объекты методы вместо обычных функций из - за отсутствия более позднего (нет модулей-нет способа иметь обычные функции, только методы).
также вы можете использовать подпрограмму с привязкой к типу.
MODULE mymod
TYPE mytype
PRIVATE
INTEGER :: x
CONTAINS
PROCEDURE, PASS :: init
END TYPE
CONTAINS
SUBROUTINE init(this, i)
CLASS(mytype), INTENT(OUT) :: this
INTEGER, INTENT(IN) :: i
IF(i > 0) THEN
this%x = 1
ELSE
this%x = 2
END IF
END SUBROUTINE init
END
PROGRAM test
USE mymod
TYPE(mytype) :: x
CALL x%init(1)
END PROGRAM
INTENT(OUT)
на this
арг о init
подпрограмма, кажется, в порядке. Потому что мы ожидаем, что этот метод будет вызываться только один раз и сразу после выделения. Возможно, было бы неплохо контролировать, что это предположение не будет неправильным. Чтобы добавить логический флаг LOGICAL :: inited
to mytype
, проверьте, если это .false.
и установить его в .true.
при первой инициализации, и сделать что-то еще при попытке повторной инициализации. Я определенно помню некоторые темы об этом в группах Google... Я не могу его найти.