Программирование - это просто
Advertisement
Главная arrow Уроки Delphi arrow Delphi изнутри arrow TObject (Free, FreeAndNil, InitInstance, NewInstance, class)
25.04.2024 г.
Главное меню
Главная
Интернет магазин
Программные продукты
Биржевые роботы
Искусственный интеллект
Математика и информатика
1С:Предприятие
Уроки C#
Уроки Delphi
Уроки программирования
Web-программирование
Дизайн и графика
Компьютер для блондинок
Исходники
Статьи
Платный раздел
Рассказы про компьютеры
Хитрости и секреты
Системный подход
Размышления
Наука для чайников
Друзья сайта
Excel-это не сложно
Все о финансах
.
TObject (Free, FreeAndNil, InitInstance, NewInstance, class) Печать E-mail
Автор megabax   
26.07.2009 г.
New Page 2

TObject (Free, FreeAndNil, InitInstance, NewInstance, class)

Продолжим рассматривать методы класса TObject. Изучим метод Free:

procedure TObject.Free;
begin
    if Self <> nil then
    Destroy;
end;

По сути, это тот же деструктор, только он проверяет самого себя на пустой указатель. Многие рекомендуют использовать Free вместо Destroy. Хотя на самом деле это без разницы, потому что реально при уничтожении объекта указатель на него не обнуляется, и такая проверка не спасает от повторной попытки уничтожения объекта. Даже если мы напишем вот такой класс

TMyObject=class(TObject)
     procedure Free;
end;
 

procedure TMyObject.Free;
begin
     inherited Free;
     self:=nil;
end;

Все рано, обнуление не будет происходить, так как после вызова

 inherited Free;

команда

self:=nil;

уже не выполняется. Для того, что бы после уничтожения объекта указатель обнулялся, используйте объявленную в модуле SysUtils процедуру FreeAndNil. Вот как она реализована:

procedure FreeAndNil(var Obj);
var
      Temp: TObject;
begin
      Temp := TObject(Obj);
      Pointer(Obj) := nil;
      Temp.Free;
end;

Сперва происходит копирование указателя во временную переменную. Затем обнуляется сам указатель объекта и только после этого через временную переменную происходит вызов метода Free. Возникает вопрос, а почему не сделано так:

procedure FreeAndNil(var Obj);
begin
      Obj.Free;
      Pointer(Obj) := nil;
end;

для чего такие сложности?

А все дело в том, что если в деструкторе возникнет исключительная ситуация, то в во втором случае указатель так и не станет nil. Иными словами, это защита от сбоев.

 На прошлом уроке я приводил объявление класса TObject. Мы рассмотрели его конструктор, который ничего не делает, тоесть, в его реализации стоит заглушка. На самом же деле, при создании объекта сперва вызывается метод NewInstance, который выделяет под объект нужную область памяти, внутри него вызывается InitInstance, который заполняет поля метода NewInstance нужными полями. только после этого вызывается собственно сам конструктор,   и только после него вызывается AfterConstruction - обработчик события окончания создания объекта. На момент вызова AfterConstruction  объект уже создан.

Давайте посмотрим, как реализован NewInstance:

class function TObject.NewInstance: TObject;
begin
        Result := InitInstance(_GetMem(InstanceSize));
end;

Как видим, данный метод вызывает InitInstance, передав ему в качестве параметра адрес выделенной области памяти. Возвращает данная функция ссылку на созданный объект, прямо как конструктор. Кстати, заметили, что функция объявлена с модификатором class? Это значит, что она является классовой, то есть, существует независимо от того, создан объект или нет. Ее можно вызывать как обратившись к имени класса (TMyClass.MyMethod) так и к переменной объекта класса (MyObj.MyTethod).

Рассмотрим пример:

unit UnitClass;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type

TStaticClass=class
      Fstr:string;
      class procedure TestStaticMethod;
      procedure TestMethod;
end;

TChildClass=class(TStaticClass)
     class procedure TestStaticMethod;
end;


TfrmTestStaticMetod = class(TForm)
       btnClassAsClass: TButton;
       btnClassAsNormal: TButton;
       btтNormalAsNormal: TButton;
       btnNormalAsClass: TButton;
       btnClassAsType: TButton;
       procedure btnClassAsClassClick(Sender: TObject);
       procedure btnClassAsNormalClick(Sender: TObject);
       procedure btnNormalAsNormalClick(Sender: TObject);
       procedure btnNormalAsClassClick(Sender: TObject);
       procedure btnClassAsTypeClick(Sender: TObject);
private
    { Private declarations }
public
    { Public declarations }
end;

var
frmTestStaticMetod: TfrmTestStaticMetod;

implementation

procedure TStaticClass.TestMethod;
begin
     Fstr:='Вызван метод TestMethod';
     messageDlg(Fstr,mtInformation,[mbOk],0);
end;

class procedure TStaticClass.TestStaticMethod;
begin
       messageDlg('Вызван метод TestStaticMethod',mtInformation,[mbOk],0);
end;

class procedure TChildClass.TestStaticMethod;
begin
     messageDlg('Вызван метод TestStaticMethod дочернего класса', mtInformation,[mbOk],0);
end;


{$R *.dfm}

procedure TfrmTestStaticMetod.btnClassAsClassClick(Sender: TObject);
var obj:TStaticClass;
begin
     obj.TestStaticMethod;
end;

procedure TfrmTestStaticMetod.btnClassAsNormalClick(Sender: TObject);
var obj:TStaticClass;
begin
     obj:=TStaticClass.Create;
     obj.TestStaticMethod;
end;

procedure TfrmTestStaticMetod.btnNormalAsNormalClick(Sender: TObject);
var obj:TStaticClass;
begin
     obj:=TStaticClass.Create;
     obj.TestMethod;
end;


procedure TfrmTestStaticMetod.btnNormalAsClassClick(Sender: TObject);
var obj:TStaticClass;
begin
     obj.TestMethod;
end;

procedure TfrmTestStaticMetod.btnClassAsTypeClick(Sender: TObject);
begin
     TStaticClass.TestStaticMethod;
end;
 

procedure TfrmTestStaticMetod.btnClassAsNilClick(Sender: TObject);
var obj:TStaticClass;
begin
    obj:=TStaticClass.Create;
    obj.Free;
    obj:=TChildClass.Create;
    obj.TestStaticMethod;
end;

end.

TObject (Free, FreeAndNil, InitInstance, NewInstance, class)

* Стоит заметить, что вот такой код:

procedure TfrmTestStaticMetod.btnClassAsClassClick(Sender: TObject);
var obj:TStaticClass;
begin
     obj.TestMethod;
end;

не допускается хотя в нашем примере, он, скорее всего, будет нормально работать. Но, раз на раз не приходиться. Записывая информацию в поле не инициализируемого объекта мы "портим" память компьютера, которая не выделена под этот объекта, а значит, там находиться что то другое, и рискуем вызвать глюки.

Классовые методы мы можем вызывать даже если объект не инициализирован:

procedure TfrmTestStaticMetod.btnClassAsClassClick((Sender: TObject);
var obj:TStaticClass;
begin
     obj.TestStaticMethod;
end;

Но, с другой стороны, мы не можем обращается к полям объекта внутри классового метода, так как этот метод может быть вызван даже тогда, когда объект класса еще не создан. Если мы попробуем обратится в классовом методе к полям объекта, то при компиляции будет выдано сообщение об ошибке:

Instance variable ... inaccessible here.

На этом я закончу урок, а в следующий раз мы продолжим изучение методов TObject.


Скриншоты, помеченные знаком * , являются цитатами и иллюстрациями  программного продукта "Delphi", авторское право на который принадлежит "Borland Software Corporation".


 

 

 

Последнее обновление ( 18.05.2013 г. )
 
Пред. »
 
© 2024 Программирование - это просто
Joomla! - свободное программное обеспечение, распространяемое по лицензии GNU/GPL.
Русская локализация © 2005-2008 Joom.Ru - Русский Дом Joomla!
Design by Mamboteam.com | Powered by Mambobanner.de
Я принимаю Яндекс.Деньги