- •Теоретические основы объектно-ориентированного программирования
- •Объектная декомпозиция
- •Диаграмма обслуживания автомашин на бензоколонке
- •Диаграмма объектов имитационной модели бензоколонки
- •Объектная декомпозиция Блока колонок: 1 - когда освободится колонка? 3 - колонка свободна? 2 - освободить колонку 4 - занять колонку
- •Диаграмма объектов графического редактора
- •Диаграмма состояний интерфейса пользователя программы «Записная книжка»
- •Диаграмма объектов предметной области программы «Записная книжка»
- •Обозначение ассоциации: а - с указанием имени ассоциации и ее направления; б - с указанием имен ролей; в - с указанием множественности
- •Контекстная диаграмма классов программы «Записная книжка»
- •Объекты и сообщения
- •Типы отношений между объектами
- •Соответствие объекта-абстракции классу и объектам-переменным
- •Основные средства разработки классов
- •Иерархии классов при различных видах наследования
- •Иерархия классов Окно и Окно_меняющее_цвет
- •Иерархия классов Окно и Окно_с_текстом
- •Иерархия классов при сложном полиморфизме
- •Необходимость позднего связывания
- •Реализация механизма позднего связывания
- •Необходимость явного указания типа объекта, адресуемого указателем родительского класса
- •Вид окна сообщения
- •Структура полей класса Сообщение
- •Дополнительные средства и приемы разработки классов
- •Организация стека вызовов
- •Определение класса
- •Организация списка объектов с использованием статических компонентов класса
- •Конструкторы и деструкторы
- •Наследование
- •Иерархия классов Целое число - Вещественное число
- •Диаграмма классов с наследованием от двух классов и объектным полем
- •Иерархия с многократным наследованием
- •Исключения
- •1. Динамическая проверка типа объекта:
- •2. Динамическое переопределение типа объекта:
- •3. Динамическое определение типа объекта:
1. Динамическая проверка типа объекта:
if Sender is TButton... if(dynamic_cast<TButton*>(Sender))
{is при неудаче возвращает false, /* dynamic_cast при неудаче
а при удаче - true} возвращает NULL,a при удаче -
указатель */
2. Динамическое переопределение типа объекта:
b := Sender as TButton; TButton& ref_b = dynamic_cast
{при неудаче генерируется <TButton&> (*Sender); /* при
исключение} неудаче генерируется исключение*/
3. Динамическое определение типа объекта:
Sender.ClassName typeid(*Sender).пате();
Метод TObject.ClassName, который возвращает строку, содержащую имя реального типа объекта независимо от типа используемой переменной, имеет аналог в C++ - name(). Остальные методы аналогов не имеют, но они описаны в TObject как общедоступные и потому могут вызываться напрямую (см. § 5.4).
Обработка исключений VCL-совместимых классов. VCL-совмести-мые классы используют механизм обработки исключений Delphi Pascal. Стандартная обработка предполагает, как правило, вывод сообщений об ошибках.
C++ Builder включает классы для автоматической обработки таких исключительных ситуаций, как деление на нуль, ошибки операций с файлами, неверное преобразование типа и т. п. Все эти классы наследуются от класса Exception (см. § 5.8). Для перехвата этих исключений используется конструкция C++:
catch (<класс исключения> <&<переменная>)
Переменная, как это и принято в C++, используется для получения значений полей класса и вызова его методов, например:
void __fastcall TForm1:: ThrowException(TObject *Sender)
{try
{ throw Exception("VCL component"); }
catch(const Exception &E)
{ShowMessage(AnsiString(E.ClassName())+ E.Message); }
}
В данном примере генерируется исключение, которое тут же перехватывается и обрабатывается.
Наиболее часто используемые классы исключений перечислены в § 5.8.
При необходимости программист может создать собственный класс-исключение, например, наследуя его от одного из существующих.
Необходимо хорошо представлять себе различие между исключениями C++ и исключениями VCL:
-
Если при конструировании объекта возникает исключение, то в C++ деструкторы вызываются только для полей и базовых классов, которые были полностью сконструированы, а в VCL - в том числе и для объекта, при конструировании которого обнаружено исключение.
-
В C++ исключения могут перехватываться по ссылке, указателю или значению, а в VCL - только по ссылке или по значению. Попытки перехвата по значению приводят к синтаксической ошибке. Исключения от схем контроля или операционной системы, такие, как EAccessViolation, могут перехватываться только по ссылке.
-
Для исключений, перехваченных по ссылке, нельзя повторно генерировать throw. Последнее связано с тем, что как только исключение операционной системы или VCL распознается как исключение C++, оно уже не может быть повторено в своем качестве из блока catch.
-
Программист не должен освобождать память объекта исключения VCL после обработки исключения.
-
Генерация исключений VCL выполняется «по значению».
Для обработки исключений операционной системы, таких, как ошибки арифметики, переполнение стека, нарушение правил доступа и т. д., используется специальная предобработка и преобразование их к исключениям VCL, т. е. VCL нормально обрабатывает эти исключения:
try{ char *p = 0; *р = 0;}
catch (const EAccessViolation &e)
{<обработка исключения>}
В качестве примера разработки VCL-совместимого класса рассмотрим проектирование главного окна приложения.
Рис. 6.2. Главное окно приложения «Динамический массив»
Пример 6.7. Разработка VCL-coвместимого класса для реализации главного окна приложения «Динамический массив». Главное окно приложения «Динамический массив» должно обеспечивать возможность тестирования всех предусмотренных операций над массивом (рис. 6.2).
В процессе визуального проектирования C++ Builder автоматически строит описание класса TMainForm, куда добавляются поля-указатели на визуальные компоненты и прототипы методов обработки событий, используемых программистом для реализации данного приложения. Это описание помещается в файл Main.h:
#iinclude <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>
#include <Grids.hpp>
class TMainForm : public TForm
{
__published // опубликованные компоненты класса
TLabel *MaxSizeI.abel; // метка Максимальный размер массива
TEdit *MaxSizeЈdit; // редактор Максимальный размер массива
TBevel *Bevel1; // рамка
TButton *ModifyButton; // кнопка Изменить
TButton *InsertButton; // кнопка Вставить
TButton *DeleteButton; // кнопка Удалить
TButton *DataButton; // кнопка Изменить данные
TButton *ExitButton; // кнопка Выход
TStringGrid *DataStringGrid; // таблица для отображения вектора
TLabel *IndexLabel; // метка Индекс
TLabel *ValueLabel; // метка Значение
TEdit *IndexEdit; // редактор индекса
TEdit *ValueEdit; // редактор значения
TLabel *CommentLabel; // метка Комментарий
void __fastcall ExitButtonClick(TObject Sender); /* обработчик события "Нажатие на кнопку Выход" */
void __fastcall ModifyButtonClick(TObject *Sender); /* обработчик события "Нажатие на кнопку Изменить" */
void __fastcall DataStringGridKeyPress(TObject *Sender, char &Key);
/* обработчик события "Ввод символа" */
void __fastcall InsertButtonClick(TObject *Sender); /* обработчик события "Нажатие на кнопку Вставить" */
void__fastcall DeleteButtonClick(TObject *Sender); /* обработчик события "Нажатие на кнопку Удалить" */
void __fastcall DataButtonClick(TObject *Sender); /* обработчик события "Нажатие на кнопку Изменить данные" */
void __fastcall FormActivate(TObject *Sender); /* обработчик события "Активация формы" */
private: // внутренние компоненты класса
public: // общедоступные компоненты класса
__fastcall TMainForm(TComponent* Owner); // конструктор
__fastcall ~TMainForm(); // деструктор
};
extern PACKAGE TMainForm *MainForm; #endif
Тела обработчиков событий программируются в файле Main.cpp. Наиболее интересные фрагменты текста программы выделены (работа с множествами Delphi Pascal, обработка исключений различных типов, проверка кода нажатой клавиши, работа со строками AnsiString, динамическая проверка типа и т. п.):
#include <vcl.h>
t#pragma hdrstop
#include "Array.h"
#include "Main.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
TMainForm *MainForm;
TMasByte*A;
__fastcall TMainForm:;TMainForm(TComponent* Owner): TForm(Owner)
{A=new TMasByte(lO);}
__fastcall TMainForm::~TMainForm() { delete A;}
void __fastcall TMainForm::ExitButtonClick(TObject *Sender) { Close(); }
void __fastcall TMainForm::ModifyButtonClick(TObject *Sender)
{ short Ind; unsigned char Value; AnsiStringNum("uндeксa");
TMsgDlgButtons Setl; Setl<<mbOK; // объявить множество
try {Ind=StrToInt(IndexEdit->Text); //выполнить, контролируя исключения
Num="элемента ";
Value =StrToInt(ValueEdit- > Text) ;
A~>Modify(Ind, Value);
A->OutputMas(DataStringGrid, 0,0); }
catch (EConvertError&) /* перехватить исключение "Ошибка преобразования" */
{AnsiString s="Heвepнo введено значение ";
MessageDlg(s+Num,mtInformation,Set1, 0); }
catch (char * Mes) /* перехватить исключения от операций над динамическим массивом */
{ MessageDlg(Mes, mtInformation, Set1, 0);}
}
void __fastcall TMainForm::DataStringGridKeyPress(TObject *Sender, char &Key)
{ if (Key==VK_RETURN) // если нажата клавиша Enter
{Key=0;
try {A->lnputMas(DataStringGrid, 0,0);
TGridOptions Set1; // объявить множество
Set1 -DataStringGrid- > Options;
Set1>>goEditing>>goAlwaysShowEditor>>goTabs;
DataStringGrid->Options=Set1;
DataStringGrid->Enabled=false;
ModifyButton~>Enabled=true;
InsertButton->Enabled=true;
DeleteButton->Enabled=true;
DataButton->Enabled=true;
IndexEdit->SetFocus();
DataStringGrid->Col=0;
CommentLabel-> Visible=false; }
catch (char* Mes)
{ TMsgDlgButtons Set2; // объявить множество
Set2<<mbOK; MessageDlg(Mes,mtlnformation,Set2,0);}
}
}
void __fastcall TMainForm::lnsertButtonClick(TObject *Sender)
{ short Ind;
unsigned char Value;
AnsiString Nит("индекса");
TMsgDlgButtons Set1;
Setl<<mbOK;
try {Ind=StrToInt(IndexEdit->Text); /* выполнить, контролируя исключения */
Num="элемента ";
Value =StrTolnt(ValueEdit-> Text);
A->Insert(Ind, Value);
A->OutputMas(DataStringGrid, 0,0); }
catch (EConvertError&) /* перехватить исключение "Ошибка преобразования" */
{AnsiString s="Неверно введено значение ";
MessageDlg(s+Num, mtInformation,Setl, 0); }
catch (char * Mes) /* перехватить исключения от операций над динамическим массивом */
{ MessageDlg(Mes,mtInformation,Setl,0); }
}
void __fastcall TMainForm::DeleteButtonCtick(TObject *Sender)
{ short Ind; TMsgDlgButtons Set1;
Setl<<mbOK;
try {Ind=StrToInt(IndexEdit-> Text);
A->Delete(Ind);
A->OutputMas(DataStringGrid,0,0); }
catch (EConvertError&)
{ MessageDlg("Неверно введено значение индекса.", mtlnformation,Set 1,0);}
catch (char * Mes)
{MessageDlg(Mes,mtInformation,Setl,0); }
}
void __fastcall TMainForm::DataButtonClick(TObject *Sender)
{ FormActivate(DataButton);}
void __fastcall TMainForm::FormActivate(TObject *Sender)
{ CommentLabel-> Visible=true;
if (dynamic_cast<TButton*> (Sender)) // если отправитель - кнопка, то
{for (int i=0;i<10;i++) DataStringGrid->Cells[i][0]="";
TGridOptions Setl; Setl =DataStringGrid->Options;
Setl << goEditing<<goAlwaysShowEditor<<go Tabs;
DataStringGrid->Options =Setl;
DataStringGrid->Enabled=true;
DataStringGrid->Col=0;
DataStringGrid->SetFocus();
delete A;
A=new TMasByte(10); /* пересоздать вектор */ }
}