Не можна, але можна в Delphi, або аномалії Delphi і чому це працює, Комерція, Різне, статті

Мене зацікавив один момент в Delphi – чому, коли я забуваю створити об’єкт класу, програма вивалюється, тим не менше, тільки всередині методу?


Я вирішив протестувати цю особливість у різних варіаціях:


TForm1




type
TSomeClass = class (TObject)
private
FProperty : Integer;
function SomeFunc(pParam : Integer; var pSelf : TSomeClass) : Integer;
end;
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Edit2: TEdit;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
xObj : TSomeClass;
x, y : Integer;
begin
x := StrToInt(Edit1.Text);
xObj.SomeFunc(x, xObj);
y := xObj.FProperty;
xObj.Free;
Edit2.Text := IntToStr(y);
end;
function TSomeClass.SomeFunc(pParam : Integer; var pSelf : TSomeClass): Integer;
var
i : Integer;
begin
Self := TSomeClass.Create;
pSelf := Self;
Result := 0;
for i := 1 to pParam do
Result := Result + i;
FProperty := Result;
end;

Наведений код компілюється і працює абсолютно без помилок (по крайней мере, в Delphi 6). Чому? Зараз будемо розбиратися.


Зверніть увагу на опис наступного класу:


TSomeClass




  TSomeClass = class (TObject)
private
FProperty : Integer;
function SomeFunc(pParam : Integer; var pSelf : TSomeClass) : Integer;
end;

І властивість, і метод його оголошені як private. Але, тим не менш, наступний код цілком чудово компілюється:


TForm1.Button1Click




procedure TForm1.Button1Click(Sender: TObject);
var
xObj : TSomeClass;
x, y : Integer;
begin
x := StrToInt(Edit1.Text);
xObj.SomeFunc(x, xObj);
y := xObj.FProperty;
xObj.Free;
Edit2.Text := IntToStr(y);
end;

Як видно, доступ до властивості і методу абсолютно вільний. Але це – широко відома аномалія захисту Delphi, яка полягає в тому, що код має доступ до всіх (навіть приватним) даним і методам класів, описаних в тому ж модулі.


Далі – більше цікавий момент. У другій сходинці викликається метод неіснуючого об’єкта! Це вже хитрість, пов’язана з особливістю компіляції класів. Справа в тому, що при компіляції класу в код все наявні у нього функції відразу компілюються у функції з неявним аргументом – посиланням Self. Так от, цей Self і є посилання на пам’ять, відведену під член-дані об’єкта. І ця пам’ять виділяється при виклику функцій, позначених як constructor, І вивільняється при виклику функцій, позначених як destructor! Виходить, що всі методи в об’єкті – це майже методи класів (Class methods, в термінології Delphi), за винятком параметра Self! Т. е. різниця між ними тільки в синтаксисі виклику і в неявному параметрі. Деструктори відрізняються від них тим, що неявно містять код звільнення пам’яті. Відповідно, конструктори не містять Self як аргумент, але повертають його після виділення пам’яті під дані об’єкта.


Тепер давайте сфокусуємося на методі класу:


TSomeClass.SomeFunc




function TSomeClass.SomeFunc(pParam : Integer; var pSelf : TSomeClass): Integer;
var
i : Integer;
begin
Self := TSomeClass.Create;
pSelf := Self;
Result := 0;
for i := 1 to pParam do
Result := Result + i;
FProperty := Result;
end;

Він містить одну локальну змінну, пам’ять під яку знову ж виділяється при компіляції класу! Тепер ми створимо об’єкт класу стандартним конструктором і привласнимо Self посилання на цей об’єкт. Знову все працює! Delphi дозволяє модифікувати змінну Self! Саме це і дозволяє написати останню сходинку в цій функції, адже тепер-то пам’ять під дані у нас виділена! Посилання на цю область пам’яті ми і повернемо через параметр pSelf.


Повернемося знову до процедури TForm1.Button1Click. Так як після виклику методу xObj.SomeFunc (x, xObj) змінна xObj містить посилання на виділену область пам’яті, доступ до xObj.FProperty у нас вже є – ми отримали від операційної системи 4 байтікі, необхідні для її зберігання. Тому y: = xObj.FProperty працює теж без проблем. Ну і, нарешті, xObj.Free повертає взяту нами пам’ять назад операційної системі. Після цього, незважаючи на те, що там лежать обчислені нами дані, доступу туди у нас вже немає (звернення до xObj.FProperty буде викликати помилку доступу до пам’яті Access Violation). Саме тому програма падає вже всередині методів, навіть якщо пам’ять під об’єкт не була виділена.


Цю статтю ні в якому разі не можна розглядати, як заклик користуватися цими аномальними особливостями Delphi, просто автор сподівається, що вона допоможе вам у подальшому виловити ті помилки, які сама Delphi зловити ніяк не може.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*