Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Delphi Pascal RTTI C++ RTTI




Try

Try

{(try {r=f/g; }

__except(EXCEPTION_EXECUTE_HANDLER)

{ throw (MyExceptionC'Ошибка Деление на нуль")); }

}

catch (const MyException& e)

{ ShowMessage(AnsiString(e.msg()));}

}

__finally { ShowMessage ("Завершающая обработка ");}

return r;

}

#pragma package(smart_init)

 

Вызов функции можно осуществить следующим образом:

 

RezEdit- > Text=FloatToStr(func(StrToFloat(DivEdit-> Text),
StrToFloat(DvEdit->Text)));

Примечание. При отладке программ, использующих обработку исключений, следует иметь в виду, что среда C++ Builder, как и среда Delphi, фиксирует любые исключения, в том числе и обрабатываемые в программе. Получив сообщение среды, необходимо продолжить выполнение программы.

6.3. VCL-совместимые классы

Для поддержки библиотеки компонентов VCL, реализованной на Delphi Pascal, были реализованы VCL-совместимые классы. Так же, как и в Delphi Pascal, они наследуются от класса TObject. Для описания таких классов в базовую объектную модель C++ были добавлены следующие средства:

- возможность объявления опубликованных и OLE-совместимых секций в классе (__published, __automated);

- группа специальных модификаторов (__declspec);

- обеспечение совместимости по типам данных и параметров;

- совместимые средства обработки исключений.

Определение VCL-совместимого класса. На VCL-совместимые классы накладываются следующие ограничения:

1. при их объявлении не разрешается использовать множественное наследование (в том числе и виртуальные базовые классы);

2. объекты VCL-совместимых классов должны создаваться динамически с использованием оператора new, а уничтожаться delete;

3. эти классы обязательно должны иметь деструктор;

4. для таких классов компилятор автоматически не создает копирующего конструктора и не переопределяет операцию присваивания.

 

Определение VCL-совместимого класса в C++ Builder выглядит следующим образом:

class <имя объявляемого класса>: <вид наследования> <имя родителя>

{ private: <скрытые элементы класса>

protected: <защищенные элементы класса>

public: <общедоступные элементы класса>

__published: <опубликованные элементы класса>

__ automated: <элементы, реализующие ОLЕ-механизм>

};

По сравнению со стандартной моделью C++ при объявлении VCL-совместимого класса можно объявлять секции published и automated в соответствии с моделью, используемой Pascal Delphi.

Секция__ published (см. § 5.1) используется для свойств, которые доступны через Инспектор объектов, если соответствующий класс включен в Палитру компонентов. В остальном опубликованные свойства идентичны общедоступным. Единственное отличие опубликованных элементов от общедоступных заключается в том, что для опубликованных элементов дополнительно генерируется RTTI информация (информация о типе времени выполнения), которая позволяет приложению динамически запрашивать элементы данных, методы и свойства ранее неизвестных типов. Опубликованные свойства могут определяться только в классе, наследуемом от TObject.

В секции опубликованных элементов не разрешается объявлять конструкторы и деструкторы. Там допустимо описывать свойства, элементы, наследуемые от объявленных в VCL (методы и указатели на них). Причем описываемые поля должны быть объектными, а свойства - простыми (не свойствами-массивами и не индексными свойствами) следующих типов: порядковые, вещественные, строки, множества, объекты или указатели на методы.

Секция __ automated используется для описания элементов, реализующих OLE-механизм.

Описатель __declspec. Он используется в C++ Builder для вызова макроопределений поддержки VCL, описанных в sysdefs.h. Аргументы этого описателя рассмотрены ниже:

__ declspec(delphiclass) - используется для описания классов, производных от TObject (см. пример 6.6);

__ declspec(delphireturn) - предназначен для описания классов, созданных в C++Builder для поддержки встроенных типов Delphi Pacal, которые не имеют аналогов в C++, таких, как Currency, AnsiString, Variant, TDateTime, Set, и используется только VCL. Посредством этого объявления классы помечаются как VCL-совместимые при использовании в качестве параметров и возвращаемых значений функций VCL. Этот модификатор необходим, когда некая структура «по значению» передается между Delphi Pascal и C++;

__ declspec(dynamic) - используется только для объявления динамических функций (см. § 5.2) в классах, наследуемых от TObject.

Пример 6.6. Статические, виртуальные и динамические полиморфные методы. В классе classl объявляются статический statfunc(), виртуальный virtfunc() и динамический dynfunc() полиморфные методы. Класс class2 перекрывает эти методы своими.

 

Описание классов выполнено в файле Object.h:

class declspec(delphiclass) classl: public TObject

{public:

char* polyfunc();

virtual char* virtfunc();

__ declspec(dynamic) char* dynfunc();

};

class __declspec(delphiclass) class2: public class1

{public:

char* polyfunc(); char* virtfunc(); char* dynfunc();

};

 

Тела функций описаны в файле Object.cpp:

#include "Object.h"

char* class1::polyfunc() {return "статический 1";}

char* class1::virtfunc() {return "виртуальный 1"; }

char* class1::dynfunc() {return "динамический 1";}

char* class2::polyfunc() {return "статический 2";}

char* class2::virtfunc() {return "виртуальный 2";}

char* class2::dynfunc() {return "динамический 2"; }

 

Затем создается динамический объект типа class2 и его адрес присваивается указателю на базовый класс:

class2 * Class2 = new class2;

class1 * Class1 = Class2;

 

Теперь при обращении к каждой из трех функций будут получены следующие результаты:

Edit->Text=Classl->polyfunc(); // статический 1 (!!!)

Edit->Text=Class2->polyfunc(); // статический 2

Edit->Text=Classl->virtfunc(); // виртуальный 2

Edit->Text=Class2->virtfunc(); // виртуальный 2

Edit->Text=Classl->dynfunc(); // динамический 2

Edit->Text=Class2->dynfunc(); // динамический 2

 

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

__ declspec(hidesbase) - используется в C++, когда необходимо указать компилятору, что данная виртуальная функция, несмотря на то же имя и тот же список параметров, образует новое семейство виртуальных функций. Необходимость такого описания вызвана тем, что в Delphi Pascal перекрывающие аспекты виртуальных функций описываются override, в то время как повторное использование описания virtual для функции с тем же именем соответствует объявлению нового семейства виртуальных функций. Для отображения этого случая в C++ и используется описатель hidesbase.

Так, если в классе Т1 описана виртуальная функция func без аргументов, а в классе Т2 необходимо описать функцию с тем же именем и также без аргументов, которая никак не связана с func, то эта функция описывается HIDESBASE:

virtual void Tl::func(void);

HIDESBASE void T2::func(void);

При отсутствии специального описателя функция T2::func() будет интерпретироваться как аспект виртуальной функции Tl::func().

__ declspec(package) - указывает, что код определения класса должен компилироваться в пакет. Пакет представляет собой DLL специального вида, используемую приложениями C++ Builder.

__ declspec(pascalimplementation) - указывает, что код, определяющий класс, был реализован в Delphi Pascal. Этот описатель используется в заголовочных файлах с расширением.hpp, описывающих макросы совместимости с библиотекой VCL.

__declspec (dllimport) - применяется для описания прототипов функций DLL.

Для статического добавления DLL к приложению C++Builder необходимо в файле проекта приложения с расширением.bpr внести имя импортируемой библиотеки в список библиотек, ассоциированный с переменной ALLLIB. При необходимости путь к этой библиотеке указывается в списке L опции переменной LFLAGS (опции компоновщика). После этого экспортируемые функции DLL становятся доступны приложению. Прототипы функцийDLL предваряются описателем __declspec(dllimport), например:

__ declspec(dllimport)

<тип результата> <имя импортируемой функции>(<параметры>);

Для динамической загрузки DLL во время работы приложения используется функция Windows API LoadLibrary() и функция GetProcAddress() - для получения адреса конкретной функции.

Совместимость по типам данных. В связи с тем, что некоторые типы по-разному определены в Delphi Pascal и C++, возможно появление трудноуловимых ошибок, вызванных различием в определении типов.

Булевы типы. Значение «истина» для типов Delphi Pascal ByteBool, WordBool и LongBool представляется минус единицей, «ложь» - нулем. Значения для типа Boolean остались традиционные: «истина» - единица, а «ложь» — нуль. C++ правильно интерпретирует эти значения. Проблема возникает, если функция WinAPI или другая функция возвращает результат типа BOOL (в котором «истина» кодируется единицей). Сравнение результата с переменными указанных типов произойдет только, если оба сравниваемых значения равны 0. Если необходимо выделить ситуации совпадения значений, то такое сравнение следует программировать как!А =!В:

A:ByteBool BOOL В А=В !А=!В
0 (False) 0 (False) 0=0 (True) !0=!0 (True)
0 (False) 1 (True) 0=1 (False) !0 =!1 (False)
-l(True) 0 (False) -1=0 (False) !-l =!0 (False)
-1 (True) 1 (True) -1=1 (False!) !-l =!1 (True)

 

Символьные типы. В C++ символьный тип - знаковый, а в Delphi Pascal - беззнаковый. Вероятность появления ошибок, вызванных этим различием, невелика.

Открытые массивы. Для обеспечения независимости процедур и функций с параметрами-массивами от размера этих массивов в Pascal используются «открытые массивы». В аналогичных случаях C++ передает адрес массива и значение последнего индекса (п-1).

Так, описанию

function Mean(Data: array of Double): Extended;

в C++ будет соответствовать описание:

Extended __fastcall Mean(const double * Data, const int Data_Size);

Вызов этой функции в C++ можно выполнить одним из следующих способов:

double d[] = { 3.1, 4.4, 5.6 }; long double x = Mean(d, 2);

или

long double у = Mean(d, (sizeof(d) / sizeof(d[0])) -1);

или

long double z = Mean(d, ARRAYSIZE(d) -1);

 

В последнем случае используется макрос ARRAYSIZE, определенный в sysdefs.h.

RTTI. Delphi Pascal содержит конструкции, использующие RTTI (см. § 5.4). C++Builder предлагает в аналогичных случаях свои конструкции:

 

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:

1. Если при конструировании объекта возникает исключение, то в C++ деструкторы вызываются только для полей и базовых классов, которые были полностью сконструированы, а в VCL - в том числе и для объекта, при конструировании которого обнаружено исключение.

2. В C++ исключения могут перехватываться по ссылке, указателю или значению, а в VCL - только по ссылке или по значению. Попытки перехвата по значению приводят к синтаксической ошибке. Исключения от схем контроля или операционной системы, такие, как EAccessViolation, могут перехватываться только по ссылке.

3. Для исключений, перехваченных по ссылке, нельзя повторно генерировать throw. Последнее связано с тем, что как только исключение операционной системы или VCL распознается как исключение C++, оно уже не может быть повторено в своем качестве из блока catch.

4. Программист не должен освобождать память объекта исключения
VCL после обработки исключения.

5. Генерация исключений 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 __f astcall DataStringGridKeyPress(TObject *Sender, char &Key);

/* обработчик события "Ввод символа" */

void __fastcall InsertButtonClick(TObject *Sender); /* обработчик события "Нажатие на кнопку Вставить" */

void__fastcall DeleteButtonClick(TObject *Sender); /* обработчик события "Нажатие на кнопку Удалить" */

void __f astcall DataButtonClick(TObject *Sender); /* обработчик события "Нажатие на кнопку Изменить данные" */

void __fastcall FormActivate(TObject *Sender); /* обработчик события "Активация формы" */

private: // внутренние компоненты класса

public: // общедоступные компоненты класса

__f astcall TMainForm(TComponent* Owner); // конструктор

__f astcall ~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 __f astcall TMainForm::ExitButtonClick(TObject *Sender) { Close(); }

void __f astcall 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 __f astcall 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 __f astcall 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); /* пересоздать вектор */ }

}

 




Поделиться с друзьями:


Дата добавления: 2014-12-08; Просмотров: 721; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.151 сек.