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

TObject (constructor, destructor memavail, GlobalMemoryStatus).

Скачать исходник к уроку.

TObject является базовым классом в Delphi. Он объявлен в модуле System следующим образом:

TObject = class
constructor Create;
procedure Free;
class function InitInstance(Instance: Pointer): TObject;
procedure CleanupInstance;
function ClassType: TClass;
class function ClassName: ShortString;
class function ClassNameIs(const Name: string): Boolean;
class function ClassParent: TClass;
class function ClassInfo: Pointer;
class function InstanceSize: Longint;
class function InheritsFrom(AClass: TClass): Boolean;
class function MethodAddress(const Name: ShortString): Pointer;
class function MethodName(Address: Pointer): ShortString;
function FieldAddress(const Name: ShortString): Pointer;
function GetInterface(const IID: TGUID; out Obj): Boolean;
class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;
class function GetInterfaceTable: PInterfaceTable;
function SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): HResult; virtual;
procedure AfterConstruction; virtual;
procedure BeforeDestruction; virtual;
procedure Dispatch(var Message); virtual;
procedure DefaultHandler(var Message); virtual;
class function NewInstance: TObject; virtual;
procedure FreeInstance; virtual;
destructor Destroy; virtual;
end;
 

Глаза в кучу от этого ужаса, не правда ли? Но на самом деле, если разобраться, ничего страшного здесь нет. Давайте пойдем по порядку и начнем с конструктора Create. В TObject он реализован вот так

constructor TObject.Create;
begin
end;

Иными словами, не реализован вообще, то есть, просто поставлена заглушка. А для чего вообще нужен конструктор? В нем производятся действия по инициализации объекта, то есть, создаются другие объекты, если это нужно, выделятся память под буферы и так далее. Для того, что бы лучше понять, для чего же нужен конструктор, давайте рассмотрим пример.

Создадим класс для работы с динамическими массивами (не важно, что в Delphi это уже есть, мы рассмотрим просто учебный пример).  В разделе interface сразу после слова type введем следующий текст:

TBufferDoubleArray=array[0..900000000] of double;
PBufferDoubleArray=^TBufferDoubleArray;


TNumberDinamicArray=Class
private
       FBuffer:PBufferDoubleArray;
       FNumbersCount:LongInt;
protected
      procedure Put(Index:integer; ANumber:double);
      procedure SetNumbersCount(ANumbersCount:LongInt);
      function Get(Index:integer):double;
public
    constructor Create(ANumbersCount:LongInt);
    destructor Destroy;
    property Items[Index: LongInt]: Double read Get write Put; default;
    property NumbersCount:integer read FNumbersCount write SetNumbersCount;
end;
 

Прежде чем мы займемся реализацией класса, я немного прокомментирую код. Сперва мы объявляем собственный тип TBufferDoubleArray - это массив  чисел с индексом от 0 до 900000000. Реально этих чисел будет меньше, и памяти мы будем выделять не на 900000001 число, а на то, сколько у нас реально будет в массиве. Далее мы объявляем тип указателя на массив. Указатели отличаются от переменной тем, что переменная - это само содержимое памяти,а указатель - это только адрес этого содержимого. Тоесть, если сравнить переменную объявленную как TBufferDoubleArray с домом, то PBufferDoubleArray - это у нас не сам дом, а только его адрес. написанный на бумажке, и мы ходим с этой бумажкой и ищем его. Если дом снесли, то мы будем искать его бесконечно, либо придем на развалины. Тоже самое случиться и с компьютером, если программист с указателями накосячит - программа будет виснуть и глючить. поэтому с указателями нужно быть очень и очень внимательными. Далее мы объявляем класс, в него включаем скрытие переменные (раздел private). Скрытые они потому, что их нельзя менять произвольно, а только через методы класса, которые не только меняют значение скрытых полей, но еще и делают действия, которые необходимы для нормальной работы программы. Конечно, можно обойтись и без скрытых полей, но тогда программисту каждый раз при изменении полей придется писать код для этих действий, что во первых, неудобно, а во вторых, программист может банально забыть это сделать и тогда его программа будет глючить.

В секции protected объявлены методы, которые недоступны из вне, но их можно использовать и переопределять в классах, объявленных как наследуемые от данного. В этом разделе у нас процедуры и функции для работы со скрытыми переменными класса.

Ну, и в разделе public доступные извне методы и свойства. В частности, конструктор и деструктор. Свойство Items служит для доступа к ячейке динамического массива по индексу через [], для этого оно объявлено как default, что  бы не писать a.items[i], а писать a[i]. NumbersCount - это у нас количество элементов в массиве, равно скрытому полю FNumbersCount.

Объявив класс, мы может объявить переменную этого класса, для этого после слова Var вставим объявление переменной данного класса*:

Delphi изнутри. TObject. (constructor, destructor)

А вот теперь реализация класса. Вставим этот код в раздел implementation:

procedure TNumberDinamicArray.Put(Index:LongInt; ANumber:double);
begin
if (Index<0) and (Index>=FNumbersCount) then
            Raise Exception.Create('TNumberDinamicArray.Put: Выход за границы массива')
else
            FBuffer^[Index]:=ANumber;
end;

function TNumberDinamicArray.Get(Index:LongInt):double;
begin
if (Index<0) or (Index>=FNumbersCount) then
          Raise Exception.Create('TNumberDinamicArray.Get: Выход за границы массива')
else
         Result:=FBuffer^[Index];
end;

procedure TNumberDinamicArray.SetNumbersCount(ANumbersCount:LongInt);
begin
        FNumbersCount:=ANumbersCount;
end;

constructor TNumberDinamicArray.Create(ANumbersCount:LongInt);
begin
       FNumbersCount:=ANumbersCount;
       GetMem(FBuffer,FNumbersCount*Sizeof(Double));
       if FBuffer=nil then Raise Exception.Create('TPassPriceSource.Create: Ошибка выделения памяти');
end;

destructor TNumberDinamicArray.Destroy;
begin
      if FBuffer<>nil then FreeMem(FBuffer);
end;
 

Теперь немного комментариев к реализации. В методах get и put мы обращаемся к элементу массива. Сперва естественно проверяем, что бы у нас не было выхода за границу массива. Если у нас все таки происходит такой выход, генерируем исключительную ситуацию, дабы не допустить чтение или запись в эту область памяти. Чем это чревато? Непредсказуемыми последствиями. У нас в памяти хранятся программы и данные, и если мы произвольно изменим их, что получиться? В лучшем случае будет выдано сообщение Invalid floating pointer operator, в худшем неверно работающая программа, зависание, другие глюки, даже может потеряться важная информация.

Значок ^ у нас означает, что переменная является указателем, то есть не самими данными, а лишь их адресом. Если его не поставить, тогда программа будет думать, что указатель - это  есть переменная. Если туда записать что то, то можно  испортить его (указатель), и даже другие области памяти со всеми вытекающими отсюда прелестями.

В конструкторе мы запоминаем переданный параметр - количество числе в массиве в скрытое поле и выделяем необходимую память. Если по какой то причине у нас выделение памяти не произошло, например, нет столько свободной памяти в компьютере, сколько мы хотим выделить (FBuffer - буфер при этом оказался пустым указателем), то мы генерируем исключительную ситуацию, дабы остановить программу и избежать возможности записывать данные в произвольную область памяти поверх других данных.

В деструкторе мы проверяем, не является ли у нас адрес буфера пустым указателем, например, если память вдруг не выделилась на этапе создания объекта, и, если указатель действительно указывает на какую то область памяти, то мы освобождаем ее.

Теперь напишем демонстрационную программу для работы с динамическими массивами. Сделаем вот такую форму*

Delphi изнутри. TObject. (constructor, destructor, GlobalMemoryStatus, MemAvail)

На этой форме у нас имеется:

  • seCreateExample (TSpinEdit - поле для ввода количества числе в массиве).

  • btnCreate (TButton - кнопочка "Создать")

  • btnDelete (TButton - кнопочка "Удалить")

  • btnShow  (TButton - кнопочка "Показать")

  • lbNumbers (TListBox - поле для просмотра списка чисел).

  • btnRefresh  (TButton - кнопочка "Обновить")

  • lbPhisicalMemoryAvail (TLabel - текст для просмотра доступной физической памяти)

  • lbVirtualMemoryAvail (TLabel - текст для просмотра доступной виртуальной памяти)

Теперь создадим обработчики событий кнопок:

procedure TfrmConstructorExample.btnCreateClick(Sender: TObject);
var i:LongInt;
begin
       FArray:=TNumberDinamicArray.Create(seCreateExample.Value);
       Randomize;
       for i:=0 to FArray.NumbersCount-1 do FArray[i]:=random(10000);
       btnDelete.Enabled:=true;
end;

procedure TfrmConstructorExample.btnDeleteClick(Sender: TObject);
begin
    FArray.Destroy;
    btnDelete.Enabled:=false;
end;

procedure TfrmConstructorExample.btnShowClick(Sender: TObject);
var i:LongInt;
begin
    lbNumbers.Clear;
    for i:=0 to FArray.NumbersCount-1 do lbNumbers.Items.Add(FloatToStr(FArray[i]));
end;

procedure TfrmConstructorExample.btnRefreshClick(Sender: TObject);
var lpBuffer: TMemoryStatus;
begin
     GlobalMemoryStatus(lpBuffer);
     lbPhisicalMemoryAvail.Caption:=FloatToStr(lpBuffer.dwAvailPhys);
     lbVirtualMemoryAvail.Caption:=FloatToStr(lpBuffer.dwAvailVirtual);
end;
 

Теперь давайте запустим программу и испытаем ее.

Delphi изнутри. TObject. (constructor, destructor, GlobalMemoryStatus, MemAvail)

Попробуйте создать очень большой массив, порядка нескольких миллионов элементов. Мы увидим, что его создание значительно уменьшает объем свободной памяти, а удаление напротив, увеличивает. Иными словами, создавая массив: 

FArray:=TNumberDinamicArray.Create(seCreateExample.Value);

мы вызываем его конструктор, где происходит выделение памяти, а уничтожая:

FArray.Destroy;

вызываем его деструктор, где происходит освобождение памяти.

Если вы создадите массив несколько раз, не удаляя предыдущий, то заметите, что объем памяти уменьшился. Эта память потерянная, что бы ее вернуть, нужно, как минимум, выйти из программы, а как максимум вообще перезагрузить компьютер. Такое явление называется утечка памяти. Что бы его не допустить, каждый созданный объект должен корректно уничтожаться деструктором.

Кстати, если попытаться применить деструктор к одному и тому же объекту дважды, то будет выдано страшное сообщение об ошибке, типа такого*

Delphi изнутри. TObject. (constructor, destructor, GlobalMemoryStatus, MemAvail)

Закомментируйте в обработчике события нажатия на кнопку "Удалить" строку

btnDelete.Enabled:=false;

и проверьте это сами.

Мы с вами рассмотрели конструктор и деструтор. В следующей статье рассмотрим различные методы класса TObject и их применение на практике.


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


 

 

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