Обработка ошибок при работе с динамическими объектами




Если при попытке разместить динамический экземпляр типа «объект» свободной памяти окажется недостаточно, то вызов расширенной процедуры New сгенерирует код ошибки выполнения 203. Но если переписать системную функцию HeapFunc (см. разд. 11.5.6) таким образом, чтобы она возвращала значение 1 вместо 0, то в размещаемую ссылочную переменную в случае ошибки вернется значение nil и программа не прервется. Если при запросе памяти для объекта процедурой

New(ИмяСсылкиНаОбъект, ИмяКонструктора)

функция HeapFunc выдаст значение 1, то конструктор не будет выполняться, а в ИмяСсылкиНаОбъект запишется nil.

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

Нехватка памяти возможна и в случае статических объектов с динамическими полями. Так, при размещении конструктором динамических полей в куче может возникнуть нехватка памяти. Но, поскольку объект статический, нельзя передать сигнальное значение nil в ссылку — ее попросту нет. Вместо этого предлагается использовать имя конструктора как логическую функцию. Если внутри конструктора была вызвана процедура Fail, то в имени конструктора вернется значение False. В остальных случаях будет возвращаться значение True. Подобным способом анализа можно пользоваться и для проверки работы унаследованных конструкторов.

На рис. 13.8 приводится пример объектов (динамических и с динамическими полями) и их инициализация с обработкой возможных ошибок. {289}

TYPE VectorType = Array [ 1..1000 ] of Real; { вектор } MatrixType = Array [1..20,1..20 ] of Real; { матрица } VectorTypePtr = ^VectorType; { ссылка на вектор } MatrixTypePtr = ^MatrixType; { ссылка на матрицу } Vector = OBJECT {статический объект с динамическим полем } V: VectorTypePtr; CONSTRUCTOR Init(FillVect: Real); DESTRUCTOR Done; VIRTUAL; PROCEDURE Work; VIRTUAL; END; ComplexPtr = ^Complex; { Динамический объект с } Complex = OBJECT(Vector) { динамическими полями данных } M: MatrixTypePtr, CONSTRUCTOR Init(FillVect, FillMat: Real); DESTRUCTOR Done; VIRTUAL; PROCEDURE Work; VIRTUAL; END; { Реализация методов объектов } CONSTRUCTOR Vector.Init(FillVect: Real); VAR i: Word; { параметр цикла заполнения } BEGIN New(V); { попытка разместить V в куче } if V=nil then begin { при неудаче сделать "откат" } Vector.Done; { завершение работы объекта } Fail { объявить сбой конструктора } end; {if} for i:=1 to 1000 do { поле V создано и заполняется } V^[i]:= FillVect; END; DESTRUCTOR Vector.Done; BEGIN if V<>nil then Dispose(V) { освобождение поля V } END; PROCEDURE Vector.Work; { метод обработки поля V } BEGIN { Какие-либо действия над вектором V^ } END; {290} CONSTRUCTOR Convex.Init(FillVect, FillMat: Real); VAR i,j: Word; { параметры циклов заполнения} BEGIN if not Vector.Init(FillVect) {инициализация прародителя } then Fail; {при неудаче сделать откат } New(M); { попытка разместить M в куче } if M=nil then begin { при неудаче сделать "откат" } Complex.Done; { завершение работы объекта } Fail { объявить сбой конструктора } end; {if} for i:=1 to 20 do {поле М создано и заполняется } for j:=1 to 20 do M^[i,j]:= FillMat; END; DESTRUCTOR Complex.Done; BEGIN if M<>nil then Dispose(M); { освобождение поля M } Vector.Done { освобождение поля V } END; PROCEDURE Complex.Work; { метод обработки поля М } BEGIN { Какие-либо действия над матрицей M^ } END; {$F+} { новая функция анализа распределения памяти} FUNCTION HeapFunc(Size: Word): Integer; BEGIN HeapFunc:= 1 END; {$F-} VAR Vec: Vector; { экземпляр статического объекта} ComPtr: ComplexPtr; { ссылка на динамический объект } BEGIN HeapError:= ©HeapFunc; { подстановка функции анализа } if not Vec.Init(1.0) { инициализация поля Vec.V } then Halt(1); { реакция на неудачу операции } New(ComPtr, Init(2,3)); { размещение объекта ComPtr^ } if ComPtr=nil { реакция на неудачу операции } then Halt(2); (* Применение методов Vec.Work и ComPtr^.Work *) Vec.Done; { освобождение поля Vec.V } Dispose(ComPtr, Done); { освобождение объекта ComPtr^ } END.

Рис. 13.8{291}

Обращаем внимание на вызовы деструкторов в Vector.Init и Complex.Init перед вызовом процедуры Fail. Они нужны для отмены всех успешных размещений полей. Также важно то, что в Complex.Init вызов Vector.Init записан в выражении таким образом, что можно проверить успешность выполнения конструктора прародителя.

Функции TypeOf и SizeOf

Турбо Паскаль версии 5.5 вводит стандартную функцию

TypeOf(ИмяЭкзОбъекта_или_ИмяТипа): Pointer

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

Функция TypeOf может быть использована для проверки фактического типа экземпляра, например:

if TypeOf(Self) = TypeOf(ObjVar) then...;

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

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

Программа может сама проверять корректность объектов, анализируя их размеры. Для этого программа должна компилироваться в режиме {$R+}, который распространен на виртуальные методы. При этом генерируется вызов подпрограммы проверки правильности VMT перед каждым вызовом виртуального метода. Если контрольные значения размеров в VMT указывают на сбой, происходит фатальная ошибка 210.

Включение механизма проверки вызовов виртуальных методов замедляет работу программы и несколько увеличивает ее размер. Поэтому имеет смысл включать режим {$R+} только во время отладки программы, а для рабочей версии устанавливать режим {$R-}. {292}



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2022-11-01 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: