#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:
Я никогда явно не создавал экземпляр класса, но кажется, что 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
это имя класса (типа), а не переменная этого типа.
Пожалуйста, ознакомьтесь с документацией для получения отличного концептуального введения во все эти концепции.