Dispose — Процедура Delphi


Содержание

Является ли память выделенной Delphi ‘New’ глобально доступной для ‘Dispose’?

Я думаю о командах New и Dispose от Delphi и задавался вопросом, могу ли я использовать эти команды в других процедурах/функциях/потоках и т.д. В моем процессе.

Я хотел бы сохранить адрес TList но я немного не уверен, так как он использует ссылку var которая может быть использована для «Сохранить» фактический адрес vars. Я не хочу никаких нарушений доступа или чего-то еще. Вот мой код:

Так это было бы безопасно, так как я не распоряжаюсь им в стеке?

Динамические распределения памяти в Delphi возвращают память в кучу, а не в стек. (См. Марко Канту объяснение этих условий.) Таким образом, память будет «глобально доступна» в вашем приложении, если имеется ссылка на эту память. Процедура New() — это один из способов динамического выделения памяти. (Некоторые другие: GetMem() , назначения строк, создание объекта.)

Итак, то, что вы предлагаете, безопасно (с точки зрения отсутствия нарушений доступа).
Тем не менее, это приведет к утечке памяти, потому что, как вы ее используете, Dispose() не освободит всю память.

Когда вы назначаете строковое значение для структуры, в куче выделяется больше памяти. Когда вы позже Dispose свою структуру, точная память, выделенная в New() будет освобождена, но на низком уровне (простая ссылка указателя на память) Delphi не знает, что могут быть другие внутренние структуры, которые нуждаются в освобождении; поэтому строки просочились.

Устранение утечки памяти

Что вам нужно сделать, это MyTList.Items[i] указатель, возвращенный MyTList.Items[i] на его правильный тип. Но сначала вам нужно явно объявить тип для указателя на вашу структуру. В качестве стандартного соглашения большинство программистов Delphi объявят тип указателя с тем же именем, заменив префикс T на PIe

Затем, когда вы выполните следующее: Dispose(PMyStruct(MyTList.Items[i])) , компилятор «реконструирует» тип, на который ссылается указатель, и будет использовать эту информацию для выполнения дополнительных действий для своих управляемых типов. Дело в том, что компилятор может автоматически обрабатывать управляемые типы правильно — но только если вы дадите ему правильную информацию о структуре, которая их содержит.

Типы, на которые это влияет:

  • строки
  • динамические массивы
  • ссылки на интерфейсы (для которых требуется ссылка)
  • Также будут управляться любые косвенные ссылки на вышеупомянутые управляемые типы. Например
  • Если ссылки и интерфейс Variant или OleVariant будут управляться.
  • Если дочерняя запись в пределах записи (структуры) ссылается на управляемые типы, они будут управляться.
  • массивы строк, массивы интерфейсов. каждая строка/элемент интерфейса также будет управляться.

Учитывая, что существует много более возможных перестановок выше, безопаснее всегда обеспечивать, чтобы Dispose() знал тип, который использовался в исходном распределении New() .

Не управляемые типы

Возможно, учитывая приведенное выше обсуждение, я должен сделать конкретную квалификацию типов, которые не управляются. Можно сделать вывод (и было бы правильно), что не управляемые типы не будут автоматически освобождены, если содержащая структура Dispose() d.

Например, если ваша структура записи содержит ссылку на объект, то, поскольку ссылка на объект не является управляемым типом, вам все равно придется явно контролировать время жизни этого экземпляра объекта. (К счастью, есть много способов сделать это.)

Процедура Dispose

Процедура Dispose в Паскале выполняет освобождение памяти. Синтаксис:

Освобождение памяти в Паскале (как и в большинстве других языков) требуется после того, как динамически выделенная память становится больше не нужна.

После работы с памятью ее надо освободить, иначе после завершения программы в памяти останется «мусор». Кроме того, хотя операционная система следит за тем, чтобы освобождать память после завершения программ, делает она это, прямо сказать, не всегда хорошо (речь о Windows, про другие системы не знаю).

Поэтому могут возникать такие неприятности, как утечка памяти. Что в худшем случае может привести к полному зависанию компьютера.

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

Первая форма процедуры Dispose освобождает память, выделенную с помощью вызова процедуры New. Указатель Р должен быть типизированным. Освобожденная память возвращается в кучу.

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

Если указатель не связан с каким-либо местом в куче (например, является пустым), то при вызове процедуры Dispose возникнет ошибка времени выполнения.

Рассмотрим только пример использования первого варианта:

Где Указатель – переменная, которая ранее использовалась с процедурой New для выделения памяти. Соответственно, процедура Dispose освобождает блок памяти, выделенный ранее процедурой New.

Здесь, кроме освобождения памяти, мы ещё указателю присваиваем нулевое значение (то есть уничтожаем его). Это не обязательно, однако лучше это делать, иначе может возникнуть ошибка во время выполнения программы (в Паскале я такого не встречал, но в С++ бывало).

ПРИМЕЧАНИЕ
Если вы смутно представляете, что такое динамическая память и указатели, а такие слова как “куча” вызывают у вас недоумение, то советую ознакомиться с книгой “Куда указывают указатели”.

Процедуры работы с динамической памятью

Procedure Dispose(var P: Pointer);

Освобождает память, распределенную для динамической переменной.


После вызова Dispose значение P не определено. При включенной директиве <$I+>, вы можете использовать исключительные ситуации, чтобы обработать эту ошибку.

Procedure FreeMem(var P: Pointer [; Size: Integer]);

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

P — переменная любого типа-указателя, предварительно созданная процедурой GetMem.

Size определяет размер динамической переменной в байтах. Для корректной работы этой процедуры, Size должно быть точно равно числу байтов, предварительно распределенных для этой переменной процедурой GetMem.

FreeMem уничтожает переменную P и возвращает память «куче». Если P не указывает на память в «куче», возникает ошибка времени выполнения.

После вызова FreeMem, значение P не определено, и происходит ошибка, если Вы впоследствии ссылаетесь на P^. Вы можете использовать исключительные ситуации, чтобы обработать эту ошибку.

Procedure GetMem(var P: Pointer; Size: Integer);

GetMem создает динамическую переменную определенного размера и помещает адрес блока в переменную Р.

P — переменная любого типа-указателя. Size — выражение, определяющее размер в байтах динамической переменной. Вы должны ссылаться на созданную переменную как P^.

Procedure New(var P: Pointer);

Создает новую динамическую переменную и помещает ее адрес в переменную Р. P — переменная любого типа-указателя. Размер распределенного блока памяти равен размеру типа, на который указывает P. Если памяти недостаточно, чтобы распределить динамическую переменную, возникает исключительная ситуация EOutOfMemory.

При завершении программы, все динамические переменные, созданные процедурами New или GetMem, должны быть уничтожены соответственно процедурами Dispose или FreeMem.

Dispose — Процедура Delphi

Если кому лень особенно напрягать не придется, прошу ответить на следующий вопрос:
использую след. код (схематично):

Pobj = ^Tobj;
Tobj = record
ph:TProgressBar;
s:string;
end;

Tlst =class( TList )
public
function Add(t:Pobj):integer;override;
destructor Destroy;
.
property Items[index:integer]:Pobj read GetIndex write PutIndex;
end;

в форме в OnCreate:
lst:=TList.Create;

добавляю в список:
.
var
p:Pobj;
begin
New(p);
p^.pb:=TProgressBar.Create(frmMain);
p^.pb.Parent:=frmMain;
lst.Add(p);

в OnDestroy формы:
lst.Free;

теперь, (кто дотерпел :))), собственно вопрос:
почему (теоретически объяснить) это работает:

destructor Destroy;
var
i:integer;
begin
for i:=0 to lst.Count-1 do begin
Dispose(Items[i]);
end;
end;

т.е., почему мне не надо делать
Items[i]^.pb.Free; //?
перед Dispose(Items[i]);

>Dmitry Toropov
Вообще-то все твои ProgressBar-ы освободит форма при своём
уничножении (хотя всё это не есть хорошо).


> почему (теоретически объяснить) это работает:
>
> destructor Destroy;
> var
> i:integer;
> begin
> for i:=0 to lst.Count-1 do begin
> Dispose(Items[i]);
> end;
> end;

А почему (как тебе кажется) это не должно работать ?

То, что форма освободит — это ясно, но я динамически создаю потоки, которые взаимодействуют с progress bar»ами и их нужно бы удалять самостоятельно по мере прекращения соответствующих потоков.
Не очень понимаю, что делает TProgressBar.Create.
Получается, что когда я делаю New(p), я уже отвожу место на куче для p^.pb(progress bar»a).
В принципе, вопрос сводится именно к тому, что же делает Create.

>Dmitry Toropov
TProgressBar.Create создаёт экзепмляр класса в памяти.


> Получается, что когда я делаю New(p), я уже отвожу место
> на куче для p^.pb(progress bar»a).

Ничего подобного !
Ты отводишь место только для указателя
(p^.pb — 4 байта) на экземпляр класса, который ты ДОЛЖЕН создать через TProgressBar.Create.

из Help»а:
The New procedure creates a new dynamic variable and sets a pointer variable to point to it. P is a variable of any pointer type. The size of the allocated memory block corresponds to the size of the type that P points to. The newly created variable can be referenced as P^.
Поэтому, я думаю что динамическая память отводится именно для record со всеми ее полями ( string, TProgressBar и т.д.).
Так прав я или не прав?


> Так прав я или не прав?

Попробуй убери строку p^.pb:=TProgressBar.Create(frmMain);
и обратись к p^.pb как к объекту.
И сразу увидишь как «память отводится именно для record со всеми ее полями»


Каждый созданный объект Create должен быть освобожден Free
Каждый выделенный блок памяти New должен быть возвращен Dispose

В дельфи все переменные объектного типа
obj: TSomeObject
являются по сути замаскированными указателями на объект.
(За исключением объектов старого типа)

Поэтому в структуре полем является указатель на объект (TProgressBar), размер коего — 4 байта.

Да, здесь я согласен. Но. После New(p) отводится память для ссылки на строку и фактической строки, поскольку в Delphi это реализовано на уровне языка, но ProgressBar-то не создается автоматически, я же его создал Create»ом! И весь вопрос в том, почему освобождается (и освобождается ли) память pb^ (если можно так выразиться :-)) при Dispose(p). Т.е. то, что место под ссылку освободится — это ясно, но как освободится сам экземпляр объекта ProgressBar, если я не вызываю pb^.Free? That»s the question.

>Dmitry Toropov
В Dispose(p) созданный объект не освободится !
Будет баааааальшой memory leak !

Вдогонку. Если я буду делать
p^.pb.Free;
Dispose(p);
это будет ошибкой. Вот в чем я не могу разобраться. Правило, что каждый объект, созданный Create должен быть явно уничтожет я понял. Именно это и вызывает у меня недоумение.

Dmitry Toropov
При Dispose ничего не освобождается, кроме самой памяти, занимаемой структурой.
Менеджер памяти абсолютно в неведении, что находится в этой памяти, есть ли в ней указатели на какие-то внешние объекты, длинные строки и т.д.

Илон Маск рекомендует:  Онлайн-сервисы моментального кредитования

Кстати, строку (если это не ShortString) тоже надо освобождать — с помощью Finalize(p^);

Tobj = record
ph:TProgressBar;
s:string;
end; //Tobj
Pobj = ^Tobj;

Tlst = class(TList)
protected
procedure Put(Index: Integer; Item: PObj);
function Get(Index: Integer): PObj;
public
function AddNewItem(const AStr : string;
AOwner : TComponent ; AParent : TWinControl) : Integer;
destructor Destroy; override;
property Items[index : Integer] : PObj read Get write Put;
default;
end; //Tlst

function Tlst.AddNewItem(const AStr: string; AOwner: TComponent;
AParent: TWinControl): Integer;
var
Aobj : Pobj;
begin
Aobj := AllocMem(SizeOf(Tobj));
with Aobj^ do begin
s := AStr;
ph := TProgressBar.Create(AOwner);
ph.Parent := AParent;
end; //with
Result := inherited Add(Aobj);
end;

destructor Tlst.Destroy;
var
ii : Integer;
begin
for ii := 0 to Count — 1 do begin
if Items[ii] = nil then Continue;
with Items[ii]^ do begin
if ph <> nil then FreeAndNil(ph);
end; //with
FreeMem(Items[ii], SizeOf(Tobj));
end; //for
inherited Destroy;
end;

function Tlst.Get(Index: Integer): PObj;
begin
Result := inherited Get(Index);
end;

procedure Tlst.Put(Index: Integer; Item: PObj);
begin
inherited Put(Index, Item);
end;

Вдогонку. Если я буду делать
p^.pb.Free;
Dispose(p);
это будет ошибкой. Вот в чем я не могу разобраться. Правило, что каждый объект, созданный Create должен быть явно уничтожет я понял. Именно это и вызывает у меня недоумение.

>Прав тот, у кого больше прав


> тоже надо освобождать — с помощью Finalize(p^);

Не обязательно, в Delphi есть механизм подсчёта ссылок на String

>Dmitry Toropov
Ещё понадобится RemoveComponent.

>Не обязательно, в Delphi есть механизм подсчёта ссылок на String
Он работает, если структура объявлена статически.

> Dmitry Toropov (24.07.02 19:18)
Вдогонку. Если я буду делать
p^.pb.Free;
Dispose(p);

Это странно. Ошибка не здесь. (И кстати, какая ошибка-то?)

>Прав тот, у кого больше прав
Нет.

>>почему освобождается (и освобождается ли) память pb^ (если
>>можно так выразиться :-)) при Dispose(p).

Строка освободится, если на нее больше нет ссылок
Экземпляр класса останется
Из кучи будет удалено sizeof(Tobj)=8 байт

А аристотели фиговы все теоретизируют, опыт им не нужен.

2Skier © (24.07.02 19:23)
>Ещё понадобится RemoveComponent.

Это-то ещё зачем? — при разрушении компонент удаляется автоматически из списков Components, Classes своих Owner»a и Parent»a.

Ничего, кроме pb.Free не нужно.

Это странно. Ошибка не здесь. (И кстати, какая ошибка-то?)

Сейчас попробую снова ситуацию смоделировать .

>Прав тот, у кого больше прав

> Это-то ещё зачем? — при разрушении компонент удаляется автоматически
> из списков

Согласен. Поспешил.


>Skier © (24.07.02 19:27)
>Нет.

До чего дошёл прогресс. Сейчас проверю.

А в чем собсвенно вопрос, то? :)

>saxon © А какая, собственно, разница.

>Skier © (24.07.02 19:27)
Да, ты был прав. После вызова Dispose счетчик ссылок строки уменьшается.

Собственно, Dispose сам вызывает Finalize:

procedure _Dispose< p: Pointer; typeInfo: Pointer>;
asm
PUSH EAX
CALL _Finalize
POP EAX
CALL _FreeMem
end;

Но если бы память выделялась через GetMem или ReallocMem — тогда
без финализации не обойтись.

>Это странно. Ошибка не здесь. (И кстати, какая ошибка-то?)

Да, должен принести свои извинения, видимо вчера у меня была в чем-то другом ошибка, и я ее случайно исправил, но подумал, что сработало удаление p^.pb.Free В любом случае, в споре родилась истина (для меня ессно :))
Приношу свои благодарности и извинения

З.Ы.
я так и не понял, надо явно удалять string или нет?

Нет, не надо. Я погорячился.
Dispose вызовет Finalize сам.

Delphi – методы уничтожения объекта

Ниже перечислены методы освобождения объектов при ручном управлении памятью.

Destroy

В основе всего лежит virtual метод Destroy класса TObject

Пользуемся им, например так…

Free (с проверкой на nil)

Самый популярный способ, на мой взгляд

DisposeOf

Появился в XE4 и создан для совместимости

FreeAndNil

Не потокобезопасный (позволяет 2 потокам войти в деструктор экземпляра)

Не типобезопасный, данный код приведет к AV

Delphi. New и dispose вместо GetMem и FreeMem

Получаю пакеты по сети. Я знаю, что размер пакета не будет больше 2000 байт. Так как получение пакета в одном потоке, а обработка в другом, то мне нужно передать копию buf через TList. Соответственно, создаю буфер в одном потоке, а уничтожаю в другом.

Раньше использовал следующую связку

Как машина поймет, сколько нужно освободить, ведь размер неизвестен заранее? может сначала нужно сделать

2 ответа 2

Во-первых, писать второй аргумент у FreeMem не нужно. Т.е. должно быть так:

Менеджер памяти Delphi (код, который реализует GetMem / FreeMem ) просто запоминает размер выделенного блока в своих служебных структурах. Где и как он это делает — вам знать не требуется, это зависит от конкретной реализации менеджера памяти, которая может меняться. Вам достаточно знать, что FreeMem всегда корректно освободит память, выделенную ранее вызовом GetMem , AllocMem (а также — New , при условии, что финализацию вы сделаете сами) — разумеется, в предположении, что в FreeMem вы передаёте ровно тот же указатель, что вам вернула GetMem , AllocMem или New . Вам не нужно специально хранить размер выделенной области (если только этот размер не нужен вам для ваших личных целей).

Во-вторых, TIdBytes является динамическим массивом, т.е. это уже динамически выделяемая память в куче, аналог «старого» Buf . Не нужно делать на него указатель: это получается «указатель на указатель», лишний уровень косвенности. Передавайте просто переменную TIdBytes . Т.е. было:

Этот код работает в точности так же, как и предыдущий: данные Buf передаются по ссылке. Если вам нужна именно копия данных (не очень понятно, как вы хотели решить этот вопрос переходом к TIdBytes и/или использованием «указателя на указатель») — просто скопируйте массив:


Как машина поймет, сколько нужно освободить, ведь размер неизвестен заранее?

Dispose — это «волшебная» функция компилятора. У неё, на самом деле, два параметра. Первый передаёте вы: это указатель на блок памяти. Второй скрытно передаёт компилятор: это указатель на информацию о типе переменной. Соответственно, в информации типа указано, что лежит в переменной, так что функция Dispose будет знать как корректно освободить память. В данном случае там будет сказано: «в переменной лежит динамический массив», так что Dispose сделает ему SetLength в 0. Ну и SetLength , соответственно, знает размер массива, чтобы корректно его освободить.

Виды памяти, указатели и работа с ними в языке Delphi

В любой вычислительной системе важным ресурсом является память. Поэтому управление ею – одна из важных задач программиста. Различают два способа распределения памяти. Статическое – во время трансляции программы, что эффективно, поскольку в ходе ее выполнения на управление памятью не расходуются ресурсы системы. Альтернативный подход – динамическое управление памятью, которое осуществляется во время выполнения программы.

Если необходимый для программы объем памяти известен заранее, то можно использовать статические переменные, которые компилятор способен обработать, не выполняя программу, только на основании ее статического текста. Более рациональный подход связан с использованием динамической памяти. Это оперативная память, предоставляемая программе в ходе ее выполнения, за вычетом сегмента данных, стека и тела программы.

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

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

Для организации динамической памяти используется тип данных, называемый указателем или ссылочным типом данных. Значением указателя является адрес области памяти, содержащей переменную заранее определенного типа. В этом случае указатели называются типизированными. Для указателей область памяти выделяется статически, а для переменных, на которые они ссылаются, – динамически. Адреса задаются совокупностью двух 16-разрядных слов, которые называются сегментом и смещением. Сегмент – это участок памяти, имеющий длину 64 Кбайт и начинающийся с адреса, кратного 16. Смещение указывает, сколько байт от начала сегмента необходимо пропустить, чтобы обратиться к нужному адресу. В результате, абсолютный адрес образуется: сегмент*16+смещение.

При решении прикладных задач с использованием языков высокого уровня наиболее частые случаи, когда могут понадобиться указатели, следующие:

1. При работе с динамическими структурами данных. Память под них выделяется в ходе выполнения программы, стандартные процедуры/функции выделения памяти возвращают адрес выделенной области памяти, т.е. указатель на нее. К содержимому динамически выделенной области памяти можно обращаться только через такой указатель.

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

Все без исключения модели современных процессоров аппаратно выполняют динамическую трансляцию адресов и совместно с операционными системами обеспечивают работу программ в виртуальной памяти. Программа разрабатывается и выполняется в виртуальной памяти, адреса в которой линейно изменяются от 0 до некоторого максимального значения. Виртуальный адрес представляет собой число – номер ячейки в виртуальном адресном пространстве. Преобразование виртуального адреса в реальный выполняется аппаратно при каждом обращении по виртуальному адресу. Это преобразование происходит совершенно прозрачно для программиста, поэтому структуру виртуального адреса можно считать и его же физической структурой.

В программе на языке высокого уровня указатели могут быть типизированными и нетипизированными. При объявлении типизированного указателя определяется и тип объекта в памяти, им адресуемого. Для объявления переменных ссылочного типа используется символ « ^ », после которого указывается тип динамической (базовой) переменной.

Например:

Type ss = ^ Integer;

Здесь переменные x и y представляют собой адреса областей памяти, в которых хранятся целые число, а z – адрес области памяти, в которой хранится вещественное число. Хотя физическая структура адреса не зависит от типа и значения данных, хранящихся по этому адресу, компилятор считает указатели x, y и z, имеющими разный тип, и в Delphi оператор:

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

Нетипизированный указатель (тип pointer в Delphi) служит для представления адреса, по которому содержатся данные неизвестного типа.

Зарезервированное слово Nil обозначает константу ссылочного типа, которая ни на что не указывает.

Выделение оперативной памяти для динамической переменной базового типа осуществляется с помощью процедуры New(x), где x определен как соответствующий указатель.

Обращение к динамическим переменным выполняется по правилу: ^.

Например, x^:=15. Здесь в область памяти (два байта), адрес которой является значением указателя x, записывается число 15.

Процедура Dispose(x) освобождает память, занятую динамической переменной. При этом значение указателя x становится неопределенным.

Основными операциями, в которых участвуют указатели, являются присваивание, получение адреса, выборка.

Присваивание – это двухместная операция, оба операнда которой – указатели. Как и для других типов, операция присваивания копирует значение одного указателя в другой, в результате чего оба указателя будут содержать один и тот же адрес памяти. Типизированные указатели должны ссылаться на объекты одного и того же типа.

Илон Маск рекомендует:  Что такое код fgetc

Операция получения адреса – одноместная, ее операнд может иметь любой тип, результатом является типизированный (в соответствии с типом операнда) указатель, содержащий адрес объекта-операнда.

Операция выборки – одноместная, ее операндом является типизированный указатель, результат – данные, выбранные из памяти по адресу, заданному операндом. Тип результата определяется типом указателя-операнда.

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

К указателю можно прибавить целое число или вычесть из него целое число. Поскольку память имеет линейную структуру, прибавление к адресу числа даст адрес области памяти, смещенной на это число байт (или других единиц измерения) относительно исходного адреса. Результат операций «указатель + целое», «указатель – целое» имеет тип «указатель».

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

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

Операции адресной арифметики выполняются только над типизированными указателями. Единицей измерения в адресной арифметике является размер объекта, который указателем адресуется. Так, если переменная x определена как указатель на целое число, то выражение x+1 даст адрес, больший не на 1, а на количество байт в целом числе. Вычитание указателей также дает в результате не количество байт, а количество объектов данного типа, помещающихся в памяти между двумя адресами. Это справедливо как для указателей на простые типы, так и для указателей на сложные объекты, размеры которых составляют десятки, сотни и более байт.

Ниже приведено несколько схем, иллюстрирующих работу с указателями.


В результате выполнения следующих операций присваивания p1^:=2; p2^:=4 в выделенные участки памяти будут записаны значения 2 и 4.

В результате выполнения оператора присваивания p1^:=p2^ в участок памяти, на который ссылается указатель p1, будет записано значение 4.

После выполнения оператора присваивания p2:=p1 оба указателя будут содержать адрес первого участка памяти.

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

— подключение динамической памяти позволяет увеличить объем обрабатываемых данных;

— если потребность в каких-то данных отпала до окончания работы программы, то занимаемую ими память можно освободить для другой информации;

— использование динамической памяти позволяет создавать структуры данных переменного размера.

При этом следует понимать, что работа с динамическими данными замедляет выполнение программы, поскольку доступ к информации происходит в два этапа: сначала ищется указатель, а затем по нему – данное.

Не нашли то, что искали? Воспользуйтесь поиском:

Лучшие изречения: Только сон приблежает студента к концу лекции. А чужой храп его отдаляет. 8806 — | 7522 — или читать все.

188.64.174.135 © studopedia.ru Не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования. Есть нарушение авторского права? Напишите нам | Обратная связь.

Отключите adBlock!
и обновите страницу (F5)

очень нужно

Реализация метода Dispose Implementing a Dispose method

Реализация метода Dispose необходима для освобождения неуправляемых ресурсов, которые использует ваше приложение. You implement a Dispose method to release unmanaged resources used by your application. Сборщик мусора .NET не выделяет и не освобождает неуправляемую память. The .NET garbage collector does not allocate or release unmanaged memory.

Шаблон ликвидации объекта, именуемый также шаблоном удаления, налагает определенные правила на время жизни объекта. The pattern for disposing an object, referred to as a dispose pattern, imposes order on the lifetime of an object. Шаблон удаления используется только для объектов, осуществляющих доступ к неуправляемым ресурсам, таких как дескрипторы файлов и каналов, дескрипторы реестра, дескрипторы ожидания и указатели на блоки неуправляемой памяти. The dispose pattern is used only for objects that access unmanaged resources, such as file and pipe handles, registry handles, wait handles, or pointers to blocks of unmanaged memory. Это вызвано тем, что сборщик мусора очень эффективно удаляет неиспользуемые управляемые объекты, но не может удалить неуправляемые объекты. This is because the garbage collector is very efficient at reclaiming unused managed objects, but it is unable to reclaim unmanaged objects.

Существует два варианта шаблона удаления: The dispose pattern has two variations:

Заключение в оболочку каждого неуправляемого ресурса, используемого типом в безопасном дескрипторе (то есть в классе, производном от System.Runtime.InteropServices.SafeHandle). You wrap each unmanaged resource that a type uses in a safe handle (that is, in a class derived from System.Runtime.InteropServices.SafeHandle). В этом случае необходимо реализовать интерфейс IDisposable и дополнительный метод Dispose(Boolean) . In this case, you implement the IDisposable interface and an additional Dispose(Boolean) method. Это рекомендуемый вариант, не требующий переопределения метода Object.Finalize. This is the recommended variation and doesn’t require overriding the Object.Finalize method.

Пространство имен Microsoft.Win32.SafeHandles содержит набор классов, производных от SafeHandle. Эти классы перечислены в разделе Использование безопасных дескрипторов. The Microsoft.Win32.SafeHandles namespace provides a set of classes derived from SafeHandle, which are listed in the Using safe handles section. Если не удается найти класс, способный освободить неуправляемый ресурс, можно реализовать собственный подкласс SafeHandle. If you can’t find a class that is suitable for releasing your unmanaged resource, you can implement your own subclass of SafeHandle.

Реализация интерфейса IDisposable и дополнительного метода Dispose(Boolean) , а также переопределение метода Object.Finalize. You implement the IDisposable interface and an additional Dispose(Boolean) method, and you also override the Object.Finalize method. Необходимо переопределить метод Finalize, чтобы убедиться, что неуправляемые ресурсы удаляются, если реализация IDisposable.Dispose не вызывается объектом-получателем типа. You must override Finalize to ensure that unmanaged resources are disposed of if your IDisposable.Dispose implementation is not called by a consumer of your type. При использовании рекомендуемого метода, описанного в предыдущем пункте, класс System.Runtime.InteropServices.SafeHandle делает это от вашего имени. If you use the recommended technique discussed in the previous bullet, the System.Runtime.InteropServices.SafeHandle class does this on your behalf.

Чтобы обеспечить соответствующую очистку ресурсов, метод Dispose должен быть доступен для многократного вызова без выдачи исключения. To help ensure that resources are always cleaned up appropriately, a Dispose method should be callable multiple times without throwing an exception.

В приведенном для метода GC.KeepAlive примере показано, как чрезмерно активная сборка мусора может привести к выполнению финализатора до завершения выполнения утилизируемого объекта. The code example provided for the GC.KeepAlive method shows how aggressive garbage collection can cause a finalizer to run while a member of the reclaimed object is still executing. Рекомендуется вызывать метод KeepAlive в конце большого метода Dispose. It is a good idea to call the KeepAlive method at the end of a lengthy Dispose method.

Dispose() и Dispose(Boolean) Dispose() and Dispose(Boolean)

Интерфейс IDisposable требует реализации одного метода Dispose без параметров. The IDisposable interface requires the implementation of a single parameterless method, Dispose. Однако шаблон удаления требует реализации двух методов Dispose : However, the dispose pattern requires two Dispose methods to be implemented:

Реализация открытого невиртуального (в Visual Basic — NonInheritable ) метода IDisposable.Dispose без параметров. A public non-virtual ( NonInheritable in Visual Basic) IDisposable.Dispose implementation that has no parameters.

Защищенный виртуальный (в Visual Basic — Overridable ) метод Dispose со следующей подписью: A protected virtual ( Overridable in Visual Basic) Dispose method whose signature is:

Перегрузка Dispose() The Dispose() overload

Поскольку открытый невиртуальный ( NonInheritable в Visual Basic) метод Dispose без параметров вызывается объектом-получателем типа, его назначение состоит в том, чтобы освободить неуправляемые ресурсы и указать, что метод завершения, если он задан, не должен выполняться. Because the public, non-virtual ( NonInheritable in Visual Basic), parameterless Dispose method is called by a consumer of the type, its purpose is to free unmanaged resources and to indicate that the finalizer, if one is present, doesn’t have to run. Он имеет стандартную реализацию: Because of this, it has a standard implementation:

Метод Dispose полностью выполняет очистку объектов, поэтому сборщику мусора не требуется вызывать переопределенный метод Object.Finalize. The Dispose method performs all object cleanup, so the garbage collector no longer needs to call the objects’ Object.Finalize override. Таким образом, вызов метода SuppressFinalize не позволит сборщику мусора запустить метод завершения. Therefore, the call to the SuppressFinalize method prevents the garbage collector from running the finalizer. Если тип не имеет метода завершения, вызов метода GC.SuppressFinalize не производит эффекта. If the type has no finalizer, the call to GC.SuppressFinalize has no effect. Обратите внимание, что фактическая работа по освобождению неуправляемых ресурсов выполняется вторым перегруженным методом Dispose . Note that the actual work of releasing unmanaged resources is performed by the second overload of the Dispose method.

Перегрузка Dispose(Boolean) The Dispose(Boolean) overload

Во второй перегрузке disposing используется параметр типа Boolean, который указывает, откуда осуществляется вызов метода: из метода Dispose (значение true ) или из метода завершения (значение false ). In the second overload, the disposing parameter is a Boolean that indicates whether the method call comes from a Dispose method (its value is true ) or from a finalizer (its value is false ).

Тело метода состоит из двух блоков кода: The body of the method consists of two blocks of code:

Блок, который освобождает неуправляемые ресурсы. A block that frees unmanaged resources. Этот блок выполняется вне зависимости от значения параметра disposing . This block executes regardless of the value of the disposing parameter.


Условный блок, который освобождает управляемые ресурсы. A conditional block that frees managed resources. Этот блок выполняется, если параметр disposing имеет значение true . This block executes if the value of disposing is true . К управляемым ресурсам, которые он освобождает, могут относиться: The managed resources that it frees can include:

Управляемые объекты, реализующие IDisposable. Managed objects that implement IDisposable. Условный блок может использоваться для вызова реализации Dispose. The conditional block can be used to call their Dispose implementation. При использовании безопасного дескриптора в качестве оболочки для неуправляемого ресурса необходимо вызвать реализацию SafeHandle.Dispose(Boolean). If you have used a safe handle to wrap your unmanaged resource, you should call the SafeHandle.Dispose(Boolean) implementation here.

Управляемые объекты, которые используют большие объемы памяти или дефицитные ресурсы. Managed objects that consume large amounts of memory or consume scarce resources. При использовании метода Dispose эти объекты освобождаются быстрее, чем при недетерминированной очистке сборщиком мусора. Freeing these objects explicitly in the Dispose method releases them faster than if they were reclaimed non-deterministically by the garbage collector.

Если метод вызывается из метода завершения (т. е. если свойство disposing имеет значение false ), выполняется только тот код, который освобождает неуправляемые ресурсы. If the method call comes from a finalizer (that is, if disposing is false ), only the code that frees unmanaged resources executes. Поскольку порядок уничтожения управляемых объектов сборщиком мусора во время завершения не определен, вызов этой перегрузки Dispose со значением false предотвращает попытки метода завершения освободить управляемые ресурсы, которые уже могли быть освобождены. Because the order in which the garbage collector destroys managed objects during finalization is not defined, calling this Dispose overload with a value of false prevents the finalizer from trying to release managed resources that may have already been reclaimed.

Илон Маск рекомендует:  Использование java классов и объектов в oracle8i

Реализация шаблона удаления для базового класса Implementing the dispose pattern for a base class

При реализации шаблона удаления для базового класса необходимо следующее: If you implement the dispose pattern for a base class, you must provide the following:

Вам следует реализовать этот шаблон для всех базовых классов, которые реализуют Dispose() и не являются sealed ( NotInheritable в Visual Basic). You should implement this pattern for all base classes that implement Dispose() and are not sealed ( NotInheritable in Visual Basic).

Реализация Dispose, которая вызывает метод Dispose(Boolean) . A Dispose implementation that calls the Dispose(Boolean) method.

Метод Dispose(Boolean) , который выполняет фактическую работу по освобождению ресурсов. A Dispose(Boolean) method that performs the actual work of releasing resources.

Любой класс, производный от класса SafeHandle, который создает оболочку для неуправляемого ресурс (рекомендуется), или переопределенный метод Object.Finalize. Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. Класс SafeHandle содержит метод завершения, что освобождает разработчика от необходимости создавать его вручную. The SafeHandle class provides a finalizer that frees you from having to code one.

Вот общий шаблон реализации шаблона удаления для базового класса, который использует безопасный дескриптор. Here’s the general pattern for implementing the dispose pattern for a base class that uses a safe handle.

В предыдущем примере используется объект SafeFileHandle для иллюстрации шаблона. Вместо него может использоваться любой объект, производный от SafeHandle. The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. Обратите внимание, что в этом примере неправильно создаются экземпляры объекта SafeFileHandle. Note that the example does not properly instantiate its SafeFileHandle object.

Вот общий шаблон реализации шаблона удаления для базового класса, который переопределяет метод Object.Finalize. Here’s the general pattern for implementing the dispose pattern for a base class that overrides Object.Finalize.

В C# переопределение Object.Finalize выполняется путем определения деструктора. In C#, you override Object.Finalize by defining a destructor.

Реализация шаблона удаления для производного класса Implementing the dispose pattern for a derived class

Класс, производный от класса, реализующего интерфейс IDisposable, не должен реализовывать интерфейс IDisposable, поскольку реализация метода IDisposable.Dispose базового класса наследуется производными классами. A class derived from a class that implements the IDisposable interface shouldn’t implement IDisposable, because the base class implementation of IDisposable.Dispose is inherited by its derived classes. Вместо этого, чтобы реализовать шаблон удаления для базового класса, необходимо следующее: Instead, to implement the dispose pattern for a derived class, you provide the following:

Метод protected Dispose(Boolean) , который переопределяет метод базового класса и выполняет фактическую работу по освобождению ресурсов производного класса. A protected Dispose(Boolean) method that overrides the base class method and performs the actual work of releasing the resources of the derived class. Этот метод должен также вызвать метод Dispose(Boolean) базового класса и передать состояние удаления ресурса в качестве аргумента. This method should also call the Dispose(Boolean) method of the base class and pass its disposing status for the argument.

Любой класс, производный от класса SafeHandle, который создает оболочку для неуправляемого ресурс (рекомендуется), или переопределенный метод Object.Finalize. Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. Класс SafeHandle содержит метод завершения, что освобождает разработчика от необходимости создавать его вручную. The SafeHandle class provides a finalizer that frees you from having to code one. Если есть метод завершения, он должен вызывать перегруженный метод Dispose(Boolean) , указывая при этом аргумент disposing со значением false . If you do provide a finalizer, it should call the Dispose(Boolean) overload with a disposing argument of false .

Вот общий шаблон реализации шаблона удаления для производного класса, который использует безопасный дескриптор: Here’s the general pattern for implementing the dispose pattern for a derived class that uses a safe handle:

В предыдущем примере используется объект SafeFileHandle для иллюстрации шаблона. Вместо него может использоваться любой объект, производный от SafeHandle. The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. Обратите внимание, что в этом примере неправильно создаются экземпляры объекта SafeFileHandle. Note that the example does not properly instantiate its SafeFileHandle object.

Вот общий шаблон реализации шаблона удаления для производного класса, который переопределяет метод Object.Finalize: Here’s the general pattern for implementing the dispose pattern for a derived class that overrides Object.Finalize:

В C# переопределение Object.Finalize выполняется путем определения деструктора. In C#, you override Object.Finalize by defining a destructor.

Использование безопасных дескрипторов Using safe handles

Написание кода для метода завершения объекта является сложной задачей, которая может вызвать проблемы при неправильном выполнении. Writing code for an object’s finalizer is a complex task that can cause problems if not done correctly. Поэтому вместо реализации метода завершения рекомендуется создавать объекты System.Runtime.InteropServices.SafeHandle. Therefore, we recommend that you construct System.Runtime.InteropServices.SafeHandle objects instead of implementing a finalizer.

Классы, производные от класса System.Runtime.InteropServices.SafeHandle, упрощают проблемы времени существования объекта путем назначения и освобождения дескрипторов без прерываний. Classes derived from the System.Runtime.InteropServices.SafeHandle class simplify object lifetime issues by assigning and releasing handles without interruption. Они содержат критический метод завершения, который обязательно выполняется во время выгрузки домена приложения. They contain a critical finalizer that is guaranteed to run while an application domain is unloading. Дополнительные сведения о преимуществах использования безопасного дескриптора см. в разделе System.Runtime.InteropServices.SafeHandle. For more information about the advantages of using a safe handle, see System.Runtime.InteropServices.SafeHandle. Безопасные дескрипторы предоставляются следующими производными классами в пространстве имен Microsoft.Win32.SafeHandles: The following derived classes in the Microsoft.Win32.SafeHandles namespace provide safe handles:

Классы SafeFileHandle, SafeMemoryMappedFileHandle и SafePipeHandle — для файлов, файлов сопоставления памяти и каналов. The SafeFileHandle, SafeMemoryMappedFileHandle, and SafePipeHandle class, for files, memory mapped files, and pipes.

Класс SafeMemoryMappedViewHandle — для представлений памяти. The SafeMemoryMappedViewHandle class, for memory views.

Класс SafeRegistryHandle — для разделов реестра. The SafeRegistryHandle class, for registry keys.

Класс SafeWaitHandle — для дескрипторов ожидания. The SafeWaitHandle class, for wait handles.

Использование безопасного дескриптора для реализации шаблона удаления для базового класса Using a safe handle to implement the dispose pattern for a base class

В следующем примере показан шаблон удаления для базового класса DisposableStreamResource , который использует безопасный дескриптор для инкапсуляции неуправляемых ресурсов. The following example illustrates the dispose pattern for a base class, DisposableStreamResource , that uses a safe handle to encapsulate unmanaged resources. Он определяет класс DisposableResource , который использует SafeFileHandle для создания экземпляра объекта Stream, который представляет открытый файл. It defines a DisposableResource class that uses a SafeFileHandle to wrap a Stream object that represents an open file. Метод DisposableResource также включает свойство Size , которое возвращает общее количество байтов в файловом потоке. The DisposableResource method also includes a single property, Size , that returns the total number of bytes in the file stream.

Использование безопасного дескриптора для реализации шаблона удаления для производного класса Using a safe handle to implement the dispose pattern for a derived class

В следующем примере показан шаблон удаления для производного класса DisposableStreamResource2 , унаследованного от класса DisposableStreamResource , представленного в предыдущем примере. The following example illustrates the dispose pattern for a derived class, DisposableStreamResource2 , that inherits from the DisposableStreamResource class presented in the previous example. Класс добавляет дополнительный метод WriteFileInfo и использует объект SafeFileHandle для создания экземпляра дескриптора записываемого файла. The class adds an additional method, WriteFileInfo , and uses a SafeFileHandle object to wrap the handle of the writable file.


Dispose — Процедура Delphi

Язык:
Русский
English

Освобождает место, занятое динамической переменной в памяти.

Объявление

Procedure Dispose(Var P : Pointer [ , Destructor ]);

Режим

Windows, Real, Protected

Замечания

Не должна использоваться совместно с процедурами Mark или Release.

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

После обращения к Dispose, значение указателя P становится неопределенным и ссылаться на него является ошибкой.

Ограничения

Если P не указывает на область памяти в куче, то происходит ошибка во время выполнения программы.

См. также

Пример

Язык:
Русский
English

Является ли память выделенной Delphi ‘New’ глобально доступной для ‘Dispose’?

Я думаю о командах New и Dispose от Delphi и задавался вопросом, могу ли я использовать эти команды в других процедурах/функциях/потоках и т.д. В моем процессе.

Я хотел бы сохранить адрес TList но я немного не уверен, так как он использует ссылку var которая может быть использована для «Сохранить» фактический адрес vars. Я не хочу никаких нарушений доступа или чего-то еще. Вот мой код:

Так это было бы безопасно, так как я не распоряжаюсь им в стеке?

Динамические распределения памяти в Delphi возвращают память в кучу, а не в стек. (См. Марко Канту объяснение этих условий.) Таким образом, память будет «глобально доступна» в вашем приложении, если имеется ссылка на эту память. Процедура New() — это один из способов динамического выделения памяти. (Некоторые другие: GetMem() , назначения строк, создание объекта.)

Итак, то, что вы предлагаете, безопасно (с точки зрения отсутствия нарушений доступа).
Тем не менее, это приведет к утечке памяти, потому что, как вы ее используете, Dispose() не освободит всю память.

Когда вы назначаете строковое значение для структуры, в куче выделяется больше памяти. Когда вы позже Dispose свою структуру, точная память, выделенная в New() будет освобождена, но на низком уровне (простая ссылка указателя на память) Delphi не знает, что могут быть другие внутренние структуры, которые нуждаются в освобождении; поэтому строки просочились.

Устранение утечки памяти

Что вам нужно сделать, это MyTList.Items[i] указатель, возвращенный MyTList.Items[i] на его правильный тип. Но сначала вам нужно явно объявить тип для указателя на вашу структуру. В качестве стандартного соглашения большинство программистов Delphi объявят тип указателя с тем же именем, заменив префикс T на PIe

Затем, когда вы выполните следующее: Dispose(PMyStruct(MyTList.Items[i])) , компилятор «реконструирует» тип, на который ссылается указатель, и будет использовать эту информацию для выполнения дополнительных действий для своих управляемых типов. Дело в том, что компилятор может автоматически обрабатывать управляемые типы правильно — но только если вы дадите ему правильную информацию о структуре, которая их содержит.

Типы, на которые это влияет:

  • строки
  • динамические массивы
  • ссылки на интерфейсы (для которых требуется ссылка)
  • Также будут управляться любые косвенные ссылки на вышеупомянутые управляемые типы. Например
  • Если ссылки и интерфейс Variant или OleVariant будут управляться.
  • Если дочерняя запись в пределах записи (структуры) ссылается на управляемые типы, они будут управляться.
  • массивы строк, массивы интерфейсов. каждая строка/элемент интерфейса также будет управляться.

Учитывая, что существует много более возможных перестановок выше, безопаснее всегда обеспечивать, чтобы Dispose() знал тип, который использовался в исходном распределении New() .

Не управляемые типы

Возможно, учитывая приведенное выше обсуждение, я должен сделать конкретную квалификацию типов, которые не управляются. Можно сделать вывод (и было бы правильно), что не управляемые типы не будут автоматически освобождены, если содержащая структура Dispose() d.

Например, если ваша структура записи содержит ссылку на объект, то, поскольку ссылка на объект не является управляемым типом, вам все равно придется явно контролировать время жизни этого экземпляра объекта. (К счастью, есть много способов сделать это.)

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL