Почему этот код работает и как Delphi создает экземпляр класса в этом случае?

#class #delphi

#класс #delphi

Вопрос:

Я делаю трек Delphi в exercism и, следуя тому, как Delphi генерирует код для формы, ответил на один из основных вопросов, подобных этому:

 unit uLeap;

interface

type
  TSYear = class
    public
      { public declarations here }
      function isLeap(y: integer): boolean;
  end;

var
TYear: TSYear;

implementation

function TSYear.isLeap(y: integer): boolean;
begin
  result := ((y mod 4) = 0) and (((y mod 400) = 0) or ((y mod 100) <> 0));
end;

end.
 

код компилируется без единой жалобы, я могу запускать его шаг за шагом, и функция «isLeap» вызывается из другого модуля несколько раз таким образом:

 procedure YearTest.year_divisible_by_4_not_divisible_by_100_leap_year;
begin
  assert.IsTrue(TYear.IsLeap(1996), 'Expected ''true'', 1996 is a leap year.');
end;
...
 

Я никогда явно не создавал экземпляр класса, но кажется, что Delphi где-то это делает, может быть, при объявлении TYear? Это допустимый способ?

Несмотря на прохождение всех тестов, код был отклонен, потому что он не выполняется обычным способом. Я, конечно, в конечном итоге сделаю это по-другому, чтобы его приняли, но, помимо неправильного именования, почему это работает? Может ли этот код вызвать проблемы где-то, чего я не вижу в этом простом примере?

Комментарии:

1.docwiki.embarcadero.com/Libraries/Sydney/de/… 😉

Ответ №1:

Я никогда явно не создавал экземпляр класса, но кажется, что Delphi где-то это делает, может быть, при объявлении TYear?

Нет, Delphi АВТОМАТИЧЕСКИ НЕ создает ваш экземпляр. Когда вы объявляете переменную типа класса, это просто переменная-указатель, которая может указывать на действительный экземпляр. Но вы всегда должны создавать этот экземпляр самостоятельно и сохранять указатель в переменной:

 SYear := TSYear.Create; // create a `TSYear` object and save its address in `SYear`
 

Это допустимый способ?

Нет.

[Почему] это работает?

Потому что вам повезло: isLeap функция не обращается ни к каким полям экземпляра класса.

Может ли этот код вызвать проблемы где-то, чего я не вижу в этом простом примере?

Если бы функция использовала какие-либо поля в экземпляре класса, вы бы получили AV, если повезет, и повреждение памяти, если не повезет.

Решение состоит в том, чтобы либо создать экземпляр и использовать его:

 SYear := TSYear.Create;
try
  ShowMessage(BoolToStr(SYear.IsLeap(2000), True));
finally
  SYear.Free;
end;
 

Или, поскольку вам явно не нужны какие-либо переменные экземпляра, чтобы определить, является ли год високосным годом или нет, лучше сделать это class методом:

 type
  TSYear = class
  public
    class function IsLeap(AYear: Integer): Boolean; static;
  end;
 

Таким образом, он может быть вызван без какого-либо экземпляра класса : TSYear.IsLeap(2000) . Обратите внимание, что TSYear это имя класса (типа), а не переменная этого типа.

Пожалуйста, ознакомьтесь с документацией для получения отличного концептуального введения во все эти концепции.