Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
РУКОВОДСТВО TURBO VISION ДЛЯ C++ TV Turbo Visio...doc
Скачиваний:
2
Добавлен:
01.04.2025
Размер:
5.2 Mб
Скачать

Глава 6. Разработка надежных программ

-----------------------------------------------------------------

Обработка ошибок в управляемом событиями интерактивном ин-

терфейсе пользователя намного сложнее, чем в автономно выполняе-

мой утилите. В неинтерактивной прикладной программе вполне допус-

тимо (и этого можно ожидать), что ошибки в программе вызывают

вывод сообщения об ошибке и завершают выполнение программы. В ин-

терактивном варианте, однако, программе необходимо выполнить

восстановление после ошибок и позволить пользователю продолжить

работу. Нельзя допускать, чтобы ошибки разрушали информацию, с

которой работает пользователь, а также, чтобы они вызывали завер-

шение программ, независимо от их природы. Программа, удовлетворя-

ющая этим критериям программирования, может считаться "надежной".

Использование Turbo Vision упрощает разработку надежных

программ, поддерживает стиль программирования, который способс-

твует обнаружению ошибок и избавлению от них, особенно от трудно-

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

памяти. Это достигается при помощи концепции атомарных (элемен-

тарных) операций.

Программирование по принципу "все или ничего"

-----------------------------------------------------------------

Атомарной называется операция, которая не может быть разбита

на более мелкие операции (это касается обработки ошибок). Или же,

более конкретно для нашей ситуации, это операция, выполнение ко-

торой завершается либо совершенно неудачно, либо полностью успеш-

но. Использование атомарных операций особенно полезно при распре-

делении памяти.

Обычно программы распределяют память в виде многих небольших

участков. Например, при создании панели диалога вы выделяете па-

мять для панели диалога, а затем для каждого из элементов управ-

ления. Каждое из этих выделений может закончиться неудачей, и лю-

бой такой случай требует проверки на предмет перехода к следующе-

му распределению или прекращению работы. Если любое распределение

памяти заканчивается неудачей, то вам следует отменить выделение

удачно распределенной памяти. В идеале вы должны были бы распре-

делить весь объем памяти и проверить, какие выделения памяти за-

кончились неудачей. Для этого используется резервная область па-

мяти.

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

-----------------------------------------------------------------

Turbo Vision выделяет обособленный фиксированный участок па-

мяти (равный по умолчанию 4К) в конце динамически распределяемой

области памяти. Эта область памяти носит название резервной об-

ласти памяти. Когда процесс распределения динамически распределя-

Turbo Vision для С++ = 171 =

емой области памяти достигает данной области, функция lowMemory

Turbo Vision возвращает значение True. Это означает, что при

дальнейшем выделении памяти резко снижается надежность работы

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

чей.

Чтобы резервная область памяти эффективно выполняла свою

роль, ее размер должен быть не меньше наибольшей области памяти,

выделенной атомарной операцией распределения памяти. Другими сло-

вами, его размер должен быть достаточен для гарантии того, что

между проверками функцией lowMemory память успешно распределя-

лась. В большинстве программ достаточен объем этой области в 4К.

Классы Turbo Vision TVMemMgr и TBufListEntry обеспечивают

управление памятью на низком уровне. Turbo Vision переопределяет

операцию new таким образом, что если в динамически распределяемой

области памяти недостаточно места для выделения области памяти,

то перед аварийным завершением new, предпринимаются определенные

действия.

Во-первых, если какие-либо кэш-буферы были выделены для вы-

полнения групповых графических операций, то первый из них будет

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

Если она также будет неудачна, то операция new освободит следую-

щий кэш-буфер и т.д.. Если выделение памяти будет неудачным и

после освобождения всех кэш-буферов, то выполняется проверка ре-

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

new будет аварийно завершен. В противном случае резервная область

памяти будет освобождена и делается последняя попытка выделить

память. Если и эта попытка закончится неудачей, то операция new

аварийно завершится.

Старый, трудоемкий способ распределения памяти

Создание панели диалога при традиционном подходе к распреде-

лению памяти будет выглядеть следующим образом:

Boolean OK = True;

TDialog *pd = new TDialog ( TRect(20,3,60,10), "My dialog");

if pd != 0

{

TStaticText *control = new TStaticText(TRect(2,2,32,3);

'Do you really wish to do this?');

if (control !=0) insert(control)

else OK = False;

TButton *control = new TButton(TRect(5,5,14,7), '~Y~es',

cmYes);

if (control !=0) insert(control)

else OK = False;

TButton *control = new TButton(Trect(16,6,25,7), '~N~o',

Turbo Vision для С++ = 172 =

cmNo);

if (control !=0) insert(control)

else OK = False;

TButton *control = new TButton(TRect(27,5,36,7),

'~C~ancel", cmCancel);

if control !=0 insert(control)

else OK = False;

}

if (!OK) destroy(pd);

Обратите внимание, что переменная OK используется для обоз-

начения выделений памяти, закончившихся неудачей. Если неудача

имела место, то вся панель диалога должна быть освобождена. Пом-

ните, что освобождение панели диалога влечет за собой и освобож-

дение всех ее отображаемых элементов.

Новый, более простой способ распределения памяти

С другой стороны, при наличии резервной области памяти весь

этот блок программы можно рассматривать как атомарную операцию,

поэтому он будет выглядеть следующим образом:

TDialog *pd = new TDialog( TRect(20,3,60,10), "My dialog");

TStaticText *control = new TStaticText(TRect(2,2,32,3),

'Do you really wich to do this?');

TButton *control = new TButton(TRect(5,5,14,7), '~Y~es',

cmYes);

TButton *control = new TButton(TRect(16,6,25,7), '~N~o',

cmNo);

TButton *control = new TButton(TRect(27,5,36,7), '~C~ancel',

cmCancel);

if (lowMemory()) // проверка надежности

pool

{

destroy(pd);

outOfMemory(); // ошибка "нет памяти"

doIt = False;

}

else

doIt = (desktop->execYiew(pd) == cmYes;

Поскольку резервная область памяти достаточно велика, что

позволяет разместить в ней всю панель диалога, которая занимает

намного меньше 4К памяти, программа может принять, что все выде-

ления памяти завершились успешно. После того, как панель диалога

будет полностью размещена, выполняется проверка переменной

lowMemory, и если она имеет значение True, то выполняется осво-

бождение памяти от всей панели диалога; в противном случае эта

панель используется.

Turbo Vision для С++ = 173 =

Метод validView

Так как проверка функции lowMemory выполняется довольно

часто, TApplication включает в себя метод validView, который мо-

жет вызываться для выполнения необходимой проверки. При использо-

вании метода validView проверка lowMemory, содержащаяся в послед-

них восьми строках программы, может быть сжата в две строки:

DoIt = (validView(pd) !=0) && (deskTop->execView(pd) == cmYes);

Метод validView возвращает либо указатель на переданный

отображаемый элемент, либо 0 (нулевой указатель), если отображае-

мый элемент невозможно разместить в памяти. Если функция

lowMemory возвращает значение True, то validView позаботится об

освобождении памяти от спорного отображаемого элемента и вызове

метода OutOfMemory.

Удаление и уничтожение объектов

Как вы могли заметить, в предыдущей программе удаление объ-

екта диалога выполнялось с помощью вызова операции destroy(pd), а

не традиционной для С++ операции delete. В связи с большим числом

взаимозависимостей классов и объектов в Turbo Vision, объекты по-

рожденные от TObject, должны удаляться в определенной последова-

тельности. Для таких случаев TObject располагает статистическим

методом destroy, имеющим в качестве единственного параметра ука-

затель на TObject. Его следует вызывать вместо обычной операции

delete при освобождении памяти от объектов, непосредственно или в

конечном счете порожденных от TObject.

Операция destroy вызывает виртуальную функцию shutDown. Ме-

тод shutDown TObject переопределяется во многих порожденных от

TObject классах, выполняя удаление соответствующих объектов в

необходимой последовательности. Например, TGroup::shutDown унич-

тожает все свои подвиды, освобождает буфер и т.д. и в конце кон-

цов вызывает метод TView::shutDown для удаления самого отображае-

мого элемента. Вам не потребуется знание всего этого механизма,

если только вы не собираетесь изменять систему управления памятью

Turbo Vision. В обычных прикладных программах следует использо-

вать операцию new естественным образом и метод destroy вместо

delete для непосредственного удаления объекта, порожденного от

TObject.

Ошибки, не связанные с распределением памяти

Не все ошибки бывают связаны с памятью. Например, может пот-

ребоваться, чтобы отображаемый элемент выполнил чтение какой-либо

информации из файла, а этого файла не существует, либо он являет-

ся ошибочным. Сообщение о таком типе ошибки должно быть передано

пользователю. К счастью, validView имеет встроенную "ловушку" для

Turbo Vision для С++ = 174 =

обработки ошибок, не связанных с распределением памяти: он вызы-

вает метод отображаемого элемента valid.

Метод TView::valid по умолчанию возвращает значение True.

Метод TGroup::valid возвращает значение True лишь в том случае,

если все принадлежащие группе отображаемые подобъекты возвращают

True из через свои функции valid. Иначе говоря, группа является

допустимой, если все ее отображаемые элементы допустимы. Если вы

создаете отображаемый элемент, который может встретить ошибки, не

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

функцию valid для этого отображаемого элемента, чтобы возвращать

значение True только в случае успешного создания его экземпляра.

Функция valid может использоваться для указания того, что

отображаемый элемент по какой-либо причине не будет применяться;

например, если для него не обнаружен требуемый файл. Обратите

внимание, что вы сами определяете предмет проверки для valid и

способ его выполнения. Типичный метод valid будет выглядеть сле-

дующим образом:

virtual Boolean TMyView::valid(ushort command)

{

if ( command == cmValid && errorEncountered )

{

reportError();

return False;

}

else

return True;

}

Когда в первый раз создается экземпляр отображаемого элемен-

та, для выявления ошибок, не связанных с памятью, возникающих в

процессе создания отображаемого элемента, его метод valid будет

вызываться с параметром command = cmValid. Метод validView(X) ав-

томатически вызывает Х.valid(cmValid) и выполняет проверку ре-

зервной области памяти, поэтому вызов validView перед использова-

нием нового отображаемого элемента весьма полезен.

Вызов метода valid выполняется также при отмене модального

состояния вида, при этом параметр command является командой, вы-

зывающей отмену модального состояния (см. главу 4, "Отображаемые

элементы"). Это предоставляет вам возможность проследить такие

ситуации перед завершением вашей программы, как несохраненный

текст в окне редактирования.

Переменная ErrorEnсountered могла быть и, вероятно, является

булевским элементом данных, задаваемым и устанавливаемым в соот-

ветствии с потребностями программы. Обычно конструктор TMyView

будет присваивать errorEncountered значение False, а модули про-

верки ошибок; определяемые вашей программой, будут присваивать

ErrorEncountered значение True.

Turbo Vision для С++ = 175 =

Сообщения об ошибках

Прежде, чем метод valid возвратит значение False, пользова-

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

не будет появляться на экране. В предыдущем примере такую опера-

цию выполняет вызов reportError. Обычно в результате этого

появляется панель диалога сообщений. Следовательно, каждый от-

дельный отображаемый элемент отвечает за выдачу сообщений об

ошибках, поэтому самой программе не требуется проверять все воз-

можные ситуации.

Это является важным шагом вперед в технике программирования,

т.к. позволяет разрабатывать программу, предполагая ее безошибоч-

ное выполнение, а не обеспечивать постоянный поиск и устранение

ошибок. Объекты групп, включая прикладные программы, вообще не

нуждаются в проверке наличия ошибок, за исключением проверки при-

надлежащих им отображаемых элементов на корректность. В этом слу-

чае, группа уничтожает себя и свои отображаемые элементы и сооб-

щает владельцу об ошибке. Группа может допустить, что ее ошибоч-

ный отображаемый элемент уже сообщил пользователю о возникшей

ошибке.

Использование метода valid позволяет рассматривать создание

окон и рамок диалога как атомарные операции. Каждый отображаемый

элемент, создающий окно, можно создать без проверки на ошибки;

если при создании будет допущена ошибка, то метод valid получит

значение False. Далее окно проходит этап полного конструирования;

начиная с этого момента окно может передаваться validView. Если

какой-либо из отображаемых элементов окна является ошибочным, то

полное окно в результате проверки на корректность возвращает

False. Метод validView освободится от окна и возвратит 0 (нулевой

указатель). Вам останется лишь проверить результат, возвращаемый

из validView.

Основные потребители

-----------------------------------------------------------------

Функция valid способна обрабатывать также отображаемые эле-

менты под названием "основные потребители". Эти отображаемые эле-

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

необходимый, например, для копирования в область памяти всего со-

держимого файла. Видимые элементы, являющиеся основными потреби-

телями, сами выполняют проверку lowMemory, не дожидаясь окончания

создания, когда ее мог бы выполнить метод validView.

Если основной потребитель выходит за пределы памяти в сере-

дине процесса создания самого себя, он устанавливает флаг, указы-

вающий, что обнаружена ошибка (подобный флагу ErrorEncountered в

предыдущем примере), и прекращает дальнейшее выделение памяти.

Проверка этого флага будет выполняться в valid, и отображаемый

элемент будет вызывать MyApplication->OutOfMemory и возвращать

Turbo Vision для С++ = 176 =

False при вызове метода valid.

Это, очевидно, худший вариант, по сравнению с возможностью

убедиться в работоспособности ваших конструкторов; однако он яв-

ляется единственным способом выполнить создание отображаемых эле-

ментов, которые превосходят размер вашей резервной области памя-

ти.

Программа FILEVIEW.CPP, имеющаяся на дистрибутивных дисках,

демонстрирует использование этой техники для реализации средства

просмотра файлов, обладающего повышенной надежностью. Например,

конструктор TFileViewer присваивает isValid значение True. Если

вызов readFile закончится неудачей, то messageBox выдаст сообще-

ние "Неверный дисковод либо каталог" и присвоит isValid значение

False.

Turbo Vision для С++ = 177 =