Студопедия

КАТЕГОРИИ:


Архитектура-(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)

Переменные варьируемого типа




МНОЖECTBA

 

В C++Builder появилась возможность имитации множеств языка Pascal. Множество служит для представления набора некоторых элементов как единого целого. Область значений множества — набор всевозможных подмножеств, составленных из его элементов. Все элементы множества принадлежат однобайтовому порядковому типу (int, char или enum), который называется базовым типом множества. Количество элементов множества называется мощностью. Мощность множества в C++Builder не может превышать 256.

В C++Builder существует целый ряд предопределенных множественных типов данных, однако в реальной задаче может потребоваться создать свой класс множеств. Это делается с помощью директивы typedef и шаблонного класса Set<type, minval, maxval>. Параметр type — это базовый тип множества (int, char или тип данных, определенный с помощью enum), minval и maxval — минимальное (не меньше 0) и максимальное (не больше 255) значения, которые способны содержаться в множестве. Например, множественный тип для хранения прописных букв английского алфавита создается так:

 

typedef Set <char, 'A', 'Z' > TLetters;

 

Теперь можно объявить переменную множественного типа:

 

TLetters letters;

 

Инициализация переменной letters выполняется с помощью оператора сдвига:

 

letters <<'А' << 'В' << 'С'; // добавление в множество

 

letters >>'В''// удаление из множества

 

Бывает нужно проверить, содержится ли некоторый элемент в множестве. Для этого используется метод Contains:

 

if (letters.Contains('В')) ShowMessage("B множестве есть буква В");

 

Если нужно удалить все элементы из множества, вызовите метод Clear:

 

letters.Clear();

 

Существуют также логические операции

== равно

!= неравно

Empty() пусто

И перегруженные операции «+» «-» «*»

 

 

 

В C++Builder появился новый класс данных Variant. Объекты этого класса называются варьируемыми переменными, поскольку они могут принимать значения различных типов данных и изменять свой тип в зависимости от типа выражения, в котором используются. Следующая подпрограмма демонстрирует приимущество варьируемых переменных:

 

void VariantDemo()

{

Variant vl, v2, v3, v4;

v1 = 5; // целое число

v2 =0.8; // вещественное число

v3 = "10"; // строка

v4 = vl + v2 + v3; // вещественное число 15.8

}

 

Как вы понимаете, тип данных Variant — это не что иное, как класс объектов. Функции и операторы этого класса обеспечивают различные способы представления одних и тех же данных. Когда вы присваиваете значение переменной типа Variant, она запоминает его именно в том формате, который был указан в операторе присваивания. При участии в выражении варьируемая переменная умеет предоставить хранящиеся в ней данные в нужном формате. Благодаря этому с переменными типа Variant можно работать, не заботясь об их фактическом типе (как с переменными языка Basic).

Вы можете спросить: "А зачем в C++Builder нужен тип Variant? Ведь он нарушает принцип строгой типизации данных!"

Ответ прост: тип данных Variant введен для поддержки технологии OLE Automation, широко применяемой для взаимодействия приложений в Windows 95 и Windows NT. Переменные типа Variant могут хранить ссылки на OLE-объекты и фигурировать в качестве параметров при вызове методов OLE-объектов. Эти особенности варьируемых переменных обеспечивают вашему приложению простой и эффективный доступ к OLE-объектам других приложений (см. главу 13).

 

Основные положения технологии OLE

 

OLE — привязка и внедрение объектов (Object Linking and Embedding) — это механизм Windows, который позволяет приложениям отображать и обрабатывать данные, подготовленные с помощью других приложений. Для этого данные оформляются в виде OLE-объекта, несущего информацию о том, каким приложением он создан и редактируется. Приложение, которое обеспечивает создание и редактирование OLE-объекта, называется OLE-сервером. Приложение, которое содержит и отображает OLE-объект, называется ОLЕ-контейнером.

В форме OLE-объектов вы можете встраивать в свои приложения все что угодно: документы, электронные таблицы, диаграммы, картинки, а также любые другие данные, создаваемые OLE-серверами, такими, например, как Microsoft Word, Microsoft Excel, CorelDRAW, Corel Photo-Paint и другие. Таким образом, технология OLE открывает безграничные возможности для интеграции разнородных данных с целью создания составных документов. Типичным составным документом является документ Microsoft Word. Наряду с текстом в нем могут содержаться электронные таблицы, диаграммы, картинки, любые другие данные. Сам по себе такой документ может быть интегрирован в другой документ.

 

Варьируемые переменные могут содержать целые (short, int. Byte), вещественные (float, double), строковые (AnsiString&, wchar_t *, char *), булевы (bool, WordBool), денежные (Currency), временные (TDateTime) значения, OLE-объекты (OIe2::IUnknown *, 01e2::IDispatch *) и массивы, составленные из элементов указанных типов. Кроме того, переменные типа Variant принимают два специальных значения: Unassigned и Null.

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

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

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

 

if ((v.Type() & varTypeMask) == varString)...

 

 

Variant v;

v=100;

if(v.Type() == 3) // varlnteger имеет код 0х0003 см таблицу

ShowMessage(AnsiString(v.Type()));

 

Метод варьируемой переменной AsType позволяет преобразовать ее значение к нужному типу, например:

 

v1 = "100"; v2 = v1.AsType(varInteger);

 

Тип Variant широко применяется при построении приложений по технологии OLE Automation

 

Таблица 1. Коды и флаги варьируемых переменных.

Код типа     Значение описание
varEmpty 0х0000 Переменная содержит значение Unassigned.
varNull 0х0001 Переменная содержит значение Null.
varSmallint 0х0002 Переменная содержит значение типа short.
varlnteger 0х0003 Переменная содержит значение типа int.
varSingle 0х0004 Переменная содержит значение типа float.
varDouble 0х0005 Переменная содержит значение типа double.
varCurrency 0х0006 Переменная содержит значение типа Currency.
varDate 0х0007 Переменная содержит значение типа TOateTiaie.
varOleStr 0х0008 Переменная содержит значение типа wchar * (ссылка на строку
        формата Unicode а динамической памяти).
varDispatch 0х0009 Переменная содержит значение типа Ole2::IDispatch * (ссылка на OLE- обьект класса IDispatch).
         
varError 0x000A Переменная содержит системный код ошибки.
varBoolean 0x000B Переменная содержит значение типа WordBool.
varVariant 0x000C Элемент варьируемого массива содержит значение типа Variant (код varVariant используется только в сочетании с флагом varArray).
varUnknown 0x000D Переменная содержит значение типа Ole2::IUnknown * (ссылка на ОLЕ-обьект класса IUnknown).
varByte 0х0011 Переменная содержит значение типа Byte.
varString 0х0100 Переменная содержит значение типа AnsiString& (ссылка на строку).
Флаги:        
varTypeMask 0x0FFF Маска для выяснения типа значения.
varArray 0х2000 Переменная содержит массив значений.
varByRef 0х4000 Переменная содержит ссылку на значение.

 

ОБЪЕКТЫ В C++BUILDER

 

Объекты и компоненты

Когда прикладные программы создавались для операционной системы MS-DOS и были консольно-ориентированными, объекты казались пределом развития программирования, поскольку были идеальным средством разбиения сложных задач на простые подзадачи. Однако с появлением графических систем, в частности Windows, программирование пользовательского интерфейса резко усложнилось. Программист в какой-то мере стал дизайнером, а визуальная компоновка и увязка элементов пользовательского интерфейса (кнопок, надписей, строк редактора) начали отнимать основную часть времени. И тогда программистам пришла в голову идея визуализировать объекты, объединив программную часть объекта с его видимым представлением на экране дисплея в одно целое. То, что получилось в результате, было названо компонентом.

Компоненты C++Builder — это особые объекты, которые являются строительными кирпичиками визуальной среды разработки и приспособлены к визуальной установке свойств. Чтобы превратить объект в компонент, первый разрабатывается по определенным правилам (см. главу 13), а затем помещается в палитру компонентов. Конструируя приложение, вы берете компоненты из палитры компонентов, располагаете на форме и устанавливаете их свойства в окне инспектора объектов. Внешне все выглядит просто, но, чтобы достичь такой простоты, потребовалось доработать модель объектов в языке C++ и обеспечить функционирование объектов-компонентов уже на этапе проектирования приложения!

 

Свойства

Давайте абстрагируемся от программной реализации объектов в языке C++ и задумаемся над тем, что же они собой представляют. В общем случае каждый объект "помнит" некоторую информацию, "умеет" выполнять некоторый набор действий и характеризуется набором свойств. То, что объект "помнит", хранится в его полях данных. То, что объект "умеет делать", реализуется в виде его внутренних функций, называемых методами. Свойства объектов аналогичны свойствам, которые мы наблюдаем у обычных предметов. Значения свойств можно устанавливать и читать.

Например, объект "кнопка" имеет свойство "цвет". Кнопка запоминает значение цвета в одном из своих полей данных. При изменении значения свойства "цвет" вызывается метод, который перерисовывает кнопку.

Изначально объекты в языке C++ содержали лишь два вида элементов: поля и методы. Среда C++Builder добавила к ним еще один — свойства. При работе с объектом свойства выглядят как поля: они принимают значения и участвуют в выражениях. Но в отличие от полей свойства не занимают места в памяти, а операции их чтения и записи ассоциируются с обычными полями или методами. Это позволяет создавать необходимые "побочные эффекты" при обращении к свойствам. Например, присвоение свойству Visible значения true вызовет отображение графического объекта на экране, а присвоение значения false — исчезновение объекта с экрана.

Покажем использование свойств на следующем примере. Пусть нужно создать класс "залипающих" кнопок TPushButton, в которых свойство Pressed показывает, нажата кнопка или нет. Свойство объявляется в классе с помощью ключевого слова __ property:

class TPushButton: public TBotton

{

private:

bool FPressed;

void SetPressed (bool Value);

public:

__property bool Pressed = { read=FPressed, write=SetPressed };

};

 

После слова read указывается поле или метод, к которому происходит обращение при чтении значения свойства, а после слова write — поле или метод, к которому происходит обращение при записи значения свойства. Например, чтение свойства Pressed заменяется на чтение поля FPressed, а установка свойства Pressed — на вызов метода SetPressed. Чтобы имена свойств не совпадали с именами полей, последние принято писать с буквы F (от англ. field ).

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

Обращение к свойствам выглядит в программе как обращение к полям:

 

TPushButton *Button;

...

Button->Pressed = true; // эквивалентно Button->SetPressed (true);

...

if (Button->Pressed) // эквивалентно if {Button-->FPressed)

...

 

Добавим, что к свойствам стандартных арифметических типов применимы комбинированные операции присваивания +=, ++ и т. п. Например, если свойство Count объекта List * имеет целочисленный тип, допустимо следующее выражение:

 

List->Count++;

 

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

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

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

GetPressed — это метод чтения свойства Pressed, процедура SetPressed — это метод записи свойства Pressed:

 

classTPushButton: public TButton

{

private.:

boo l GetPressed (void);

void SetPressed (boo l Value);

рubliс:

__property bool Pressed = { read = GetPressed, write = SetPressed };

};

 

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

 

void TPushButton::SetPressed(bool Value)

{

FPressed = Value;

Invalidate();

}

 

Один и тот же метод может использоваться для получения (установки) значений нескольких свойств одного типа. В этом случае каждому свойству назначается целочисленный индекс, который передается в метод первым параметром. Например, в классе ящиков ТВох методы Getter и Setter обслуживают три свойства: Width, Height и Depth — ширину, высоту и глубину ящика:

class ТВох

{

private:

int FDimensions[3];

int Getter (int Index);

void Setter (int index, int Value);

public:

__property int Width = { index = 0, read = Getter, write = Setter };

__property iftt Height = { index = 1, read = Getter, write = Setter };

__property int Depth = { index = 2, read = Getter, write = Setter };

};

//--------------------------------------------------------------

int ТВох::Getter (int Index)

{

return FDimensions [index];

}

//---------------------------------------------------------------

void ТBох:: Setter (int Index, int Value)

{

FDimension [Index] =Value;

}

 

Обращения к свойствам Width, Height и Depth заменяются на соответствующие вызовы методов Getter и Setter, например:

 

ТВох *Вox;

...

Box->Height =100; // эквивалентно Box->Setter(1,100);

...

if (Box->Нeight < 0) // эквивалентно if(Box->Setter(1)) < 0)

 

Свойства-массивы

Кроме обычных свойств в классах объявляются еще свойства-массивы (array properties). Свойство-массив — это индексированное множество свойств. В виде свойства-массива Dimensions удобно, например, представить размеры ящика:

 

class TBox

{

__property int Dimensions[int Index] = { read = Getter, write =Setter };

};

 

Обратите внимание, что методы Getter и Setter обслуживают и свойство-массив Dimensions, и индексированные свойства Width, Height и Depth. Если в описании обычных свойств могут участвовать поля, то в описании свойств-массивов разрешено использовать только методы.

Основная выгода от применения свойств-массивов — возможность выполнения итераций с помощью цикла for, например:

 

for (int i = 0; i <= 2; i++)

{

Box->Dimensions[i] = 0;

};

 

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

Свойства-массивы имеют два важных отличия от обычных массивов:

· Их индексы не ограничиваются диапазоном и могут принадлежать любому типу данных, а не только int. Например, можно создать свойство-массив, в котором индексами будут строки. Обращение к такому свойству могло бы выглядеть так:

 

Box->Dimensions["Height"] = 100;

· Операции над свойством-массивом в целом запрещены; разрешены операции только над его элементами.

Видимость элементов объекта

Как вы знаете, в языке C++ существует возможность ограничить видимость элементов класса (полей, методов и свойств) из других классов и функций. Для этого служат директивы private, protected и public. Среда C++Builder добавила к ним еще две: __published и __automated.

 

Директива __published

Директива __published устанавливает такие же правила видимости элементов класса, как и директива public, — элементы всегда видны за пределами своего класса. В дополнение к этому для элементов, помещенных в секцию __published, компилятор генерирует специальную информацию, которая позволяет превращать объекты в компоненты визуальной среды разработки! Пример с использованием директивы __published рассмотрен в главе 9.

Например определив класс

 

,,,

enum TLabel3DStyle { IsLowered, IsRaised };

,,,

 

class TLabel3D: public TLabel

{

private:

TLabel3DStyle FStyle;

void __fastcall SetStyle(TLabel3DStyle AStyle);

void __fastcall DoDrawText();

protected:

void __fastcall Paint();

public:

__fastcall TLabel3D(TComponent* Owner);

__published:

__property TLabel3DStyle Style =

{ read=FStyle, write=SetStyle, default=lsLowered };

};

Обратите внимание на описание свойства Style. Оно объявлено в секции __published и имеет перечислимый тип (TLabel3DStyle), поэтому будет представле­но в инспекторе объектов в виде выпадающего списка. Значение свойства читается из поля FStyle, а записывается методом SetStyle.

По умолчанию надпись считается вдавленной (Islowered), на это указывает директива default в описании свойства Style. Директива default помогает среде C++Builder более эффективно работать со свойством. Если значение свойства, установленное в инспекторе объектов, совпадает со значением по умолчанию, то оно не сохраняется в DFM-файле и за инициализацию свойства отвечает конструктор.

 

Директива __automated

 

Директива __automated устанавливает такие же правила видимости элементов класса, как и директива public, — элементы всегда видны за пределами своего класса. В дополнение к этому для элементов, помещенных в секцию __automated, компилятор генерирует специальную информацию, которая обеспечивает их видимость за пределами приложения и доступные automation-контроллерам., обеспечивающие взаимодействие различных приложений на основе технологии OLE.

 

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

Пример. Определите в классе TAutoDemo в секции __automated метод ShowAboutDialog, который выдает на экран окно сообщений с информацией о сервере:

 

class TAutoDemo: public TAutoObject

{

private:

public:

__fastcall TautoDemo();

__automated:

void __fastcall ShowAboutDialog();

};

 

и запишите тело метода следующим образом:

 

void __fastcall TAutoDemo::ShowAboutDialog()

{

ShowMessage("Automation Server Demo!");

}

 

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

 

· В секции __automated разрешено объявлять свойства и методы, но не поля.

· Типы данных, используемые при объявлении свойств, параметров методов и результатов функций, должны быть из следующего спис­ка: Currency, double, int, float, short, AnsiString, TDateTime, Variant и unsigned short. Все остальные типы, включая обычные массивы, структуры и указатели, запрещены.

· В описании свойств могут встречаться лишь спецификаторы досту­па (read и write). Все остальные спецификаторы (index, stored, default, nodefault) не дозволены. Чтение и запись свойств должны всегда назначаться на методы, но не на поля. Перекрытие свойств не поддерживается.

· Использование свойств-массивов возможно.

· Все методы, в том числе методы чтения и записи свойств, должны использовать соглашение о вызовах __fastcall. Методы могут быть объявлены с директивой virtual. Перекрытие методов поддержи­вается.

· Объявления свойств и методов могут содержать необязательную директиву __dispid, за которой в скобках следует целочисленная константа. Данная директива устанавливает так называемый идентификатор диспетчеризации (dispatch ID), используемый для обра­щения к свойству или методу. Если директива __dispid отсутствует, то компилятор автоматически использует число, большее на еди­ницу самого большого идентификатора диспетчеризации, исполь­зованного в методах и свойствах как самого класса, так и его предков. Идентификаторы диспетчеризации должны быть уникаль­ными.

 

Указатели на методы объектов

Реализация механизма событийного управления потребовала от разработчиков C++Builder создать в языке C++ новый вид указателей — указатели на методы. Они объявляются с помощью ключевого слова __closure и хранят одновременно адрес объекта и адрес его метода. Например, указатель

 

void (__closure *PressMethod) (); Обрптите вынимание Два подчеркивания

 

может хранить адрес метода Press, определенного в классе TPushButton:

 

class TPushButton: public TButton

{

public: void Press();

};

 

Взгляните на следующую подпрограмму:

 

void Test (TPushButton *Button)

{

void (__closure *PressMethod) ();

PressMethod = Button ->Press;

PressMethod();

}

 

В результате выполнения оператора PressMethod() будет вызван метод Press с передачей ему в качестве псевдопараметра this объекта Button.

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

 

Особенности классов VCL

Все классы библиотеки визуальных компонентов (VCL) так или иначе наследуются от стандартного базового класса TObject. Это сделано для удобства. Например, в VCL-библиотеке существуют классы для управления динамическими массивами и списками объектов. Они оперируют объектами класса TObject, следовательно, приспособлены для работы с любыми VCL-объектами. Рекомендуется порождать свои собственные классы от TObject, чтобы пользоваться всеми возможностями VCL. Исключение составляют те случаи, когда необходимо иметь полный контроль над размещением данных объекта в оперативной памяти.

Еще одной особенностью классов VCL является отказ от множественного наследования. Это обусловлено совместимостью со средой Delphi. Однако вы свободно можете использовать множественное наследование в своих собственных классах.

 




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


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


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



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




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