Введение в программирование
Области применения языков программирования
В настоящее время языки программирования применяются в самых различных областях человеческой деятельности, таких как:С течением времени одни языки развивались, приобретали новые черты и остались востребованы, другие утратили свою актуальность и сегодня представляют в лучшем случае чисто теоретический интерес. В значительной степени это связано с такими факторами, как:
Парадигмы программирования
Синтаксис языка описывает систему правил написания различных языковых конструкций, а семантика языка программирования определяет смысл этих конструкций. Синтаксис языка программирования может быть описан с помощью НБФ-нотаций.Семантика языка взаимосвязана с используемой вычислительной моделью. В настоящее время языки программирования в зависимости от применяемой вычислительной модели делятся на четыре основные группы:
function1(function2( function3(beginning_date)));
Код программы на языке системы правил может быть записан следующим образом:
if condition1 then operator1; if condition2 then operator2; if condition3 then operator3;
В настоящий момент наибольшее распространение получили языки, основанные на объектно-ориентированной модели. Они, реализуя процедурную модель построения языка, поддерживают аппликативность конструкций, позволяя представлять блок-схему выполнения структурированной программы как некоторый набор аппликативных функций.
Первые языки программирования
В пятидесятые годы двадцатого века с появлением компьютеров на электронных лампах началось бурное развитие языков программирования. Компьютеры, стоившие в то время значительно дороже, чем разработка любой программы, требовали высокоэффективного кода. Такой код разрабатывался вручную на языке Ассемблер. В середине 50-х годов под руководством Джона Бэкуса для фирмы IBM был разработан алгоритмический язык программирования FORTRAN. Несмотря на то, что уже существовали разработки языков, выполняющие преобразование арифметических выражений в машинный код, создание языка FORTRAN (FORmula TRANslator), предоставляющего возможность записи алгоритма вычислений с использованием условных операторов и операторов ввода/вывода, стало точкой отсчета эры алгоритмических языков программирования.К языку FORTRAN предъявлялись требования cоздания высокоэффективного кода. Поэтому многие конструкции языка первоначально разрабатывались с учетом архитектуры IBM 407. Успех разработки этого языка привел к тому, что производители других вычислительных систем стали создавать свои версии трансляторов. С целью некоторой возможной на тот момент унификации языка язык FORTRAN IV, разработанный в 1966 году, стал первым стандартом, именуемым FORTRAN 66.
Как альтернатива языку FORTRAN, первоначально ориентированному на архитектуру IBM, под руководством Питера Наура в конце 50-х годов был разработан язык ALGOL (ALGOrithmic Language). Основной целью, преследуемой разработчиками этого языка, была независимость от конкретной архитектуры вычислительной системы. Кроме того, создатели языка ALGOL стремились разработать язык, удобный для описания алгоритмов и применяющий систему обозначений, близкую к той, что принята в математике.
Языки FORTRAN и ALGOL были первыми языками, ориентированными на программирование вычислений.
Язык PL/I, первые версии которого появились в начале 60-х годов, был первоначально ориентирован на IBM 360 и расширял возможности языка FORTRAN некоторыми средствами языка COBOL, разработанного в эти же годы.
Несмотря на определенную популярность языка PL/I у программистов, работавших на компьютерах IBM и машинах серии ЕС, в настоящее время он представляет чисто теоретический интерес.
В конце 60-х годов под руководством Найарда и Дала был разработан язык Simula-67, использующий концепцию пользовательских типов данных. Фактически это первый язык, применяющий понятие классов.
В середине 70-х годов Вирт предложил язык Pascal, который сразу стал широко использоваться. В это же время по инициативе Министерства обороны США началась работа по созданию языка высокого уровня, получившего название Ada – в честь Ады Лавлейс, программистки и дочери лорда Байрона. Создание языка началось с определения требований и выработки спецификаций. Над проектом работали четыре независимые группы, но все они использовали как основу язык Pascal. В начале 80-х годов был разработан первый промышленный компилятор языка Ada.
Универсальный язык программирования С был разработан в середине 70-х годов Денисом Ритчи и Кеном Томпсоном. Этот язык стал популярным языком системного программирования и в свое время использовался для написания ядра операционной системы UNIX. Стандарт языка С начал разрабатываться рабочей группой института стандартов ANSI в 1982 году. Международный стандарт языка С принят в 1990 году. Язык С лег в основу разработки языков программирования C++ и Java.
Наряду с алгоритмическими языками параллельно развивались и языки, предназначаемые для обработки деловой информации, а также языки искусственного интеллекта. К первым относится язык COBOL (COmmon Business Oriented Language), а ко вторым – языки LISP (LISt Processing) и Prolog. Язык LISP, разработанный в 60-х годах под руководством Дж. Маккарти, был первым функциональным языком обработки списков, который нашел широкое применение в теории игр.
С появлением персональных компьютеров языки стали составными частями интегрированных сред разработки. Появились языки, применяемые в различных офисных программах, например VBA (Visual Basic for Application).
В 90-х годах с распространением сети Интернет расширяется возможность распределенной обработки данных, что отражается и на развитии языков программирования. Появляются языки, ориентированные на создание серверных приложений, такие как Java, Perl и PHP, языки описания документов – HTML и XML. Традиционные языки программирования С++ и Pascal также претерпевают изменения: под языком программирования начинает пониматься не только функциональность самого языка, а также библиотеки классов, предоставляемые средой программирования. Акцент со спецификации самих языков программирования переносится на стандартизацию механизмов взаимодействия распределенных приложений. Появляются новые технологии – COM и CORBA, специфицирующие взаимодействие распределенных объектов.
Среда проектирования
С развитием языков программирования совершенствовались и средства разработки программ – от режима командной строки до интегрированной среды проектирования. Такая среда предоставляет удобный графический интерфейс разработки и большой спектр сервисов, включающих управление версиями хранимых данных, утилиты просмотра и управления информацией, библиотеки классов, мастера создания шаблонов приложений и т.п.Компилятор языка программирования выступает как составная часть среды проектирования. Сама программа наряду с конструкциями, предусмотренными стандартом, как правило, использует библиотечные функции и классы, предоставляемые средой проектирования. Так, интегрированная среда разработки VisualStudio.NET содержит библиотеку классов MFC (Microsoft Foundation Classes), значительно упрощающую процесс разработки приложений, использующих оконный интерфейс.
Интегрированная среда проектирования VisualStudio.NET позволяет создавать и компилировать приложения на языках C++, C#, Visual Basic и VisualJ. Для разработки приложений на языке С++ предназначается также среда CBuilder.
Для проектирования приложений на языке Object Pascal используется интегрированная среда проектирования Delphi.
Наиболее удобной средой разработки программ на языке Java является интегрированная среда проектирования JBuilder.

Стандартизация языков программирования
Концепция языка программирования неотрывно связана с его реализацией. Для того чтобы компиляция одной и той же программы различными компиляторами всегда давала одинаковый результат, разрабатываются стандарты языков программирования. Существует ряд организаций, целенаправленно занимающихся вопросами стандартизации. Это Американский национальный институт стандартов ANSI (American National Standards Institute), Институт инженеров по электротехнике и электронике IEEE (Institute of Electrical and Electronic Engineers), Организация международных стандартов ISO (International Organization for Standardization).Как правило, при создании языка выпускается частный стандарт, определяемый разработчиками языка. Если язык получает широкое распространение, то со временем появляются различные версии компиляторов, которые не точно следуют частному стандарту. В большинстве случаев идет расширение зафиксированных первоначально возможностей языка. Для приведения наиболее популярных реализаций языка в соответствие друг с другом разрабатывается согласительный стандарт. Очень важным фактором стандартизации языка программирования является своевременность появления стандарта – до широкого распространения языка и создания множества несовместимых реализаций. В процессе развития языка могут появляться новые стандарты, отражающие современные нововведения. Так, язык FORTRAN первоначально был стандартизирован в 1966 году. В результате был издан стандарт FORTRAN 66. Далее этот стандарт несколько раз пересматривался (в 1977 году был выпущен FORTRAN 77, затем появился и FORTRAN 90).
Язык Java, ставший в последнее время весьма распространенным, постепенно был значительно расширен и модифицирован: новая спецификация получила название Java 2.
В процессе развития языка некоторые его конструкции и функции устаревают. Однако с целью обратной совместимости новые версии должны поддерживать и все устаревающие возможности. Это ведет к "разбуханию" компиляторов. В последнее время в реализациях введено понятие не рекомендуемой и устаревшей возможности. В первом случае следующий стандарт еще будет поддерживать не рекомендуемую возможность, но может перевести ее в категорию устаревшей. Во втором случае стандарт может исключить поддержку возможности, объявленной ранее как устаревшая. Введение не рекомендуемых и устаревших возможностей предоставляет разработчикам временной интервал, в течение которого они могут модифицировать код в соответствии с новыми требованиями стандарта.
Введение в программирование
НБФ-грамматика
Грамматикой называется формальное описание синтаксиса языка программирования.Грамматика определяется набором правил (называемых иногда правилами подстановки), определяющих формирование из лексем достоверных программ.
Формальная грамматика использует строгую систему обозначений. Существуют различные типы грамматик. НБФ-грамматика является контекстно-свободной грамматикой. Эта грамматика использует НБФ-нотации, предложенные Джоном Бэкусом в конце 50-х годов для описания синтаксиса языка ALGOL.
Простая НБФ-нотация позволяет описывать все достоверные конструкции языка программирования, используя следующие символы:
Значения, указываемые вне скобок < >, называются терминальными символами.
НБФ-грамматика состоит из набора правил (называемых иногда металингвистическими формулами или продукциями), записываемых при помощи НБФ-нотации.
Например:
<цифра>::= 0|1|2|3|4|5|6|7|8|9 <целочисленное значение> ::= цифра | цифра < целочисленное значение>
В данном примере символы 0, 1, 2, 3 и т.д. являются терминальными символами.
При построении грамматики, описывающей язык программирования, выделяется начальный нетерминальный символ, определяющий конструкцию "программа".
Существуют порождающие и распознающие грамматики. Порождающая грамматика генерирует множество цепочек терминальных символов из начального символа, а распознающая грамматика используется для построения по цепочке символов дерева грамматического разбора, ведущего к начальному символу.
Можно сказать, что грамматика состоит из множества терминальных и нетерминальных символов, начального нетерминального символа и набора правил.
В 1959 году Ноам Хомский предложил следующую классификацию грамматик:
Процесс трансляции
Программу, написанную на языке программирования высокого уровня, называют исходной программой, а каждую самостоятельную программную единицу, образующую данную программу, - программным модулем. Для преобразования исходной программы в ее выполняемую форму (выполнимый файл) транслятор выполняет некоторую последовательность действий. Эта последовательность зависит как от языка программирования, так и от конкретной реализации самого транслятора. В ходе трансляции важно не просто откомпилировать программу, а получить при этом достаточно эффективный код.В процессе трансляции выполняется анализ исходной программы, а затем синтез выполнимой формы данной программы. В зависимости от числа просмотров исходной программы, выполняемых компилятором, трансляторы разделяются на однопроходные, двухпроходные и трансляторы, использующие более двух проходов.
К достоинствам однопроходного компилятора можно отнести высокую скорость компиляции, а к недостаткам - получение, как правило, не самого эффективного кода.
Широкое распространение получили двухпроходные компиляторы. Они позволяют при первом проходе выполнить анализ программы и построить информационные таблицы, используемые при втором проходе для формирования объектного кода.
На рисунке 2.1 представлены основные этапы, выполняемые в процессе трансляции исходной программы.

Рис. 2.1. Основные этапы трансляции программы
Фаза анализа программы состоит из:
При анализе исходной программы транслятор последовательно просматривает текст программы, представимой как набор символов, выполняя разбор структуры программы.
На этапе лексического анализа выполняется выделение основных составляющих программы – лексем. Лексемами являются ключевые слова, идентификаторы, символы операций, комментарии, пробелы и разделители. Лексический анализатор не только выделяет лексемы, но и определяет тип каждой лексемы. При этом на этапе лексического анализа составляется таблица символов, в которой каждому идентификатору сопоставлен свой адрес.
Это позволяет при дальнейшем анализе вместо конкретного значения (строки символов) использовать его адрес в таблице символов.
Процесс выделения лексем достаточно трудоемок и требует применения сложных контекстно-зависимых алгоритмов.
На этапе синтаксического анализа выполняется разбор полученных лексем с целью получения семантически понятных синтаксических единиц, которые затем обрабатываются семантическим анализатором. Так, синтаксическими единицами выступают выражения, объявление, оператор языка программирования, вызов функции.
На этапе семантического анализа выполняется обработка синтаксических единиц и создание промежуточного кода. В зависимости от наличия или отсутствия фазы оптимизации результатом семантического анализа может быть оптимизируемый далее промежуточный код или готовый объектный модуль.
К наиболее общим задачам, решаемым семантическим анализатором, относятся:
Макросом называется некоторый предварительно определенный код, который на этапе компиляции вставляется в программу во всех местах указания вызова данного макроса.
На фазе синтеза программы производится:
Процесс генерации кода состоит из преобразования промежуточного кода (или оптимизированного кода) в объектный код. При этом в зависимости от языка программирования получаемый объектный код может быть представлен в выполнимой форме или как объектный модуль, подлежащий дальнейшей обработке редактором связей.
Так, процесс генерации кода является неотъемлемой частью фазы синтеза программы, а необходимость выполнения редактора связей зависит от конкретного языка программирования. Следует учесть, что на практике термин "генерация кода" часто применяют ко всем действиям фазы синтеза программы, ведущим к получению выполнимой формы программы.
Редактор связей приводит в соответствие адреса фрагментов кода, расположенных в отдельных объектных модулях: определяются адреса вызываемых внешних функций, адреса внешних переменных, адреса функций и методов каждого модуля. Для редактирования адресов редактор связей использует специальные, создаваемые на этапе трансляции, таблицы загрузчика. После обработки объектных модулей редактором связей генерируется выполнимая форма программы.
Расширенная НБФ-нотация
При описании правил НБФ-грамматики с применением стандартной НБФ-нотации синтаксические конструкции, имеющие необязательные или альтернативные элементы, выглядят при всей их простоте достаточно громоздко. Расширенная НБФ-нотация вводит ряд дополнительных элементов, позволяющих значительно улучшить наглядность представления правил НБФ-грамматики.Расширенная НБФ-нотация вводит следующие дополнительные элементы:

Трансляторы
Программа, написанная на языке высокого уровня, перед исполнением должна быть преобразована в программу на "машинном языке". Такой процесс называется трансляцией, или компиляцией. По типу выходных данных различают два основных вида трансляторов:Окончательным выполнимым кодом являются приложения, реализованные как EXE-файлы, DLL-библиотеки, COM-компоненты. К интерпретируемому коду можно отнести байт-код JAVA-программ, выполняемый посредством виртуальной машины JVM.
Языки, формирующие окончательный выполнимый код, называются компилируемыми языками. К ним относятся языки С, C++, FORTRAN, Pascal. Языки, реализующие интерпретируемый код, называются интерпретируемыми языками. К таким языкам относятся язык Java, LISP, Perl, Prolog.
В большинстве случаев код, получаемый в результате процесса трансляции, формируется из нескольких программных модулей. Программным модулем называется определенным образом оформленный код на языке высокого уровня. Процесс трансляции в этом случае может выполняться как единое целое – компиляция и редактирование связей, или как два отдельных этапа – сначала компиляция объектных модулей, а затем вызов редактора связей, создающего окончательный код. Последний подход более удобен для разработки программ. Он реализован в трансляторах языков С и С++.
Объектный код, создаваемый компилятором, представляет собой область данных и область машинных команд, имеющих адреса, которые в дальнейшем "согласуются" редактором связи (иногда называемым загрузчиком). Редактор связи размещает в едином адресном пространстве все по отдельности откомпилированные объектные модули и статически подключаемые библиотеки.
Будем называть выполнимой формой программы код, получаемый в результате трансляции исходной программы.
Введение в программирование
Операции
При вычислении выражений учитывается приоритет операций: сначала выполняются операции с более высоким приоритетом.Вычисление выражений, имеющих операции с одинаковым приоритетом, производится в соответствии с правилом сочетательности, которое определяет порядок выполнения таких операций. В языке С сочетательность операций применяется как слева направо, так и справа налево (как и при вычислении возведения в степень). Порядок вычисления справа налево означает, что выражение x** 2**4 трактуется как x**(2**4).
В следующей таблице приведены в убывающем порядке уровни приоритета операций языка С.
| Скобки | ( ), [ ] | слева направо |
| Индексы, вызов функций | x[i], fun() | слева направо |
| Постфиксный инкремент и декремент | ++, -- | слева направо |
| Префиксный инкремент и декремент | ++, -- | слева направо |
| Унарный минус | - | слева направо |
| Поразрядное отрицание (NOT) | ~ | слева направо |
| Размер объекта | sizeof | слева направо |
| Логическое отрицание | ! | слева направо |
| Получение адреса и разименование | &, * | справа налево |
| Явное приведение типа | (any_type) | слева направо |
| Умножение, деление, деление по модулю | *, / , % | слева направо |
| Сложение, вычитание | +, - | слева направо |
| Сдвиг влево, сдвиг вправо | <<, >> | слева направо |
| Сравнение (меньше, больше, меньше или равно, больше или равно) | <, >, <=, >= | слева направо |
| Сравнение (тождественное равенство, неравенство) | ==, != | слева направо |
| Поразрядное логическое И | & | слева направо |
| Поразрядное исключающее ИЛИ (XOR) | ^ | слева направо |
| Поразрядное логическое ИЛИ | | | слева направо |
| Логическое И (AND) | && | слева направо |
| Логическое ИЛИ (OR) | || | слева направо |
| Условная операция | ?: | справа налево |
| Операция перед присваиванием | =, +=, -=, *=, /=, %=, <<=, >>=, &=, ^=, |= | справа налево |
Операции AND, OR, NOT и XOR относятся к логическим операциям. Следующая таблица показывает результаты применения логических операций.
| 0 (false) | 0 (false) | 0 | 0 | 1 | 0 |
| 0 (false) | 1 (true) | 0 | 1 | 1 | 1 |
| 1 (true) | 0 (false) | 0 | 1 | 0 | 1 |
| 1 (true) | 1 (true) | 1 | 1 | 0 | 0 |
Единственной операцией, имеющей три операнда, является операция "условие" (называемая также условной операцией).
Условная операция имеет следующий формальный синтаксис:
(expr_log) ? expr1:expr2.
Если выражение expr_log принимает значение true, то условная операция возвращает значение expr1, а если false, то значение expr2.
Например:
// x примет значение 1, если y>z. x=(y>z)? 1:0;
Операции сдвига целочисленного значения выполняют сдвиг всех битов операнда, указанного слева, на число позиций, заданных операндом, указанным справа, а вместо сдвинутых битов записываются нули. Операции сдвига имеют следующий формальный синтаксис:
value<
Например, выражение 9<<2 вычисляется следующим образом: число 9 имеет в двоичном представлении значение 001001 (118) и при сдвиге его на два разряда влево получается значение 100100 (448).
Операции инкремента и декремента соответственно увеличивают или уменьшают значение операнда на 1. Различают постфиксный и префиксный инкремент и декремент. Например, выражение x++ возвращает значение переменной х, а затем увеличивает его на 1, а выражение ++x увеличивает значение x на 1 и возвращает его.
Операция присваивания в различных языках имеет разное обозначение. Так, в языках С, C++, Java операция присваивания обозначается символом =. Например, x=y+z;. Язык С позволяет в одном операторе указывать несколько операций присваивания. Например: x1=x2=y+z;. В языках Pascal и ALGOL операция присваивания указывается символами :=. Например: x:=y+z;. В языке LISP операция присваивания обозначается функцией SETQ (например, (SETQ x (PLUS y z))).
Операторы цикла
Операторы цикла наряду с механизмом рекурсии выражают форму повторения последовательности действий.Языки программирования, как правило, имеют несколько форм оператора цикла.
В языке С++ предусмотрено три формы оператора цикла:
Цикл for выполняется заданное число раз, а проверка условия принадлежности счетчика цикла заданному диапазону производится до выполнения операторов, указанных в цикле.
Оператор do выполняется до тех пор, пока условие цикла остается истинным, а проверка условия цикла производится после выполнения операторов, указанных в цикле.
Оператор while выполняется до тех пор, пока условие цикла остается истинным, а проверка условия цикла производится до выполнения операторов, указанных в цикле.
Принято считать, что любой оператор цикла состоит из двух частей:
Реализация операторов цикла с конечным числом повторений отличается от реализации циклов с бесконечным повторением или повторением, основанным на некоторых данных. При реализации цикла с конечным числом повторений выделяется специальная область памяти для хранения этого значения. Цикл for также может относиться как к циклам с конечным числом повторений ( for (i=1; i<50; i++){cout<
Операторы исключений
Некоторые языки программирования позволяют реализовывать обработку ошибок, называемых исключениями, используя операторы исключений. Код, который может инициировать исключение, заключается в специальный оператор try-catch. При этом ключевое слово catch определяет действия, выполняемые в случае возникновения определенного исключения. Исключение может инициироваться программно или оператором throw (бросок исключения). Некоторые языки программирования позволяют передавать обработку исключения вызывающему методу (так, в языке Java в сигнатуре метода можно ключевым словом throws указать список исключений, при возникновении которых управление будет возвращено вызывающей программе).
Операторы перехода
Для выхода из бесконечных циклов или подпрограмм используются операторы перехода. В языке C++ реализованы четыре оператора перехода:Операторы выбора
К операторам выбора относятся:Операторы выбора осуществляют ветвление. Оператор if в зависимости от значения выражения-условия позволяет выполнить только одну из двух указанных последовательностей операторов (в большинстве языков программирования такая последовательность операторов указывается как один составной оператор). Существуют формы оператора if, позволяющие задавать вместо второй выполняемой последовательности операторов условие (if-elseif-then- elseif-then).
Оператор switch в зависимости от значения вычисляемого выражения позволяет выполнить одну из нескольких указанных последовательностей операторов.
Например:
switch (i): { case 0: case 1: // последовательность операторов break; case 2: // последовательность операторов break; default: }
Реализация оператора if достаточно проста: как правило, процессор поддерживает команды перехода и ветвления.
Реализация оператора switch может быть выполнена в виде таблицы перехода, состоящей из команд безусловного перехода на соответствующие фрагменты кода. Вычисляемое выражение оператора switch в этом случае преобразовывается в значение сдвига по таблице перехода, определяющее выполняемую команду.
Определение последовательности действий в выражениях
Выражение состоит из операций, операндов и функций (функции можно рассматривать как особый тип операции). Операндами могут выступать переменные и константы. Операторы, определяющие операции, могут быть унарными и бинарными.Унарный оператор действует только на один операнд, а бинарный оператор – на два операнда.
Синтаксис выражения можно представить в виде дерева: вершиной дерева является последняя выполняемая операция, узлы описывают промежуточные операции, а листья указывают данные (переменные или константы).
На рисунке 3.1 показано древовидное представление вычисления выражения (x*y)-x*(-(x**2)+(y-0.3)).

Рис. 3.1. Древовидное представление выражения
Для представления выражения в линейной форме применяются следующие формы записи:
В префиксной записи, называемой также польской префиксной записью, сначала записывается символ операции, а затем по порядку слева направо записываются операнды. Так, выражение (z+2)*(x+y) в префиксной записи будет иметь следующий вид: * + z 2 + x y. Польская префиксная запись не содержит скобок и позволяет однозначно определять порядок вычисления выражения.
Язык LISP для представления выражений использует префиксный тип записи, называемый кембриджской польской записью. Такая запись в отличие от польской записи содержит скобки, ограничивающие операнды, на которые действует операция. Таким образом, в кембриджской польской записи выражение представляет собой множество вложенных подвыражений, ограниченных скобками.
Например:
* (+ (z 2) +(x y))
В постфиксной записи, называемой также обратной польской записью или суффиксной записью, символ операции записывается после операндов. Выражение (z+2)*(x+y) в постфиксной записи будет иметь следующий вид: z 2 + x y + *.
Третьим типом записи выражений является инфиксная запись, используемая для представления выражений как в математике, так и в языках программирования. Инфиксная запись - это стандартный способ записи выражений, при котором символ операции указывается между операндами. Однако инфиксная запись не позволяет представлять унарные операции.
Наиболее простым представлением выражения с точки зрения процесса трансляции является постфиксная запись. Однако префиксная запись более удобно обеспечивает обработку функций. Кроме того, префиксная запись позволяет вычислить выражение за один просмотр транслятора, но существенным недостатком при этом является то, что для каждой операции требуется предварительно знать число обрабатываемых ею операндов (унарная, бинарная, функция).
Составные операторы
Каждая из основных форм управления последовательностью действий имеет в различных языках программирования свои, в значительной степени синтаксически схожие, операторы языка программирования.Для создания сложных управляющих композиций иногда последовательность операторов необходимо указывать как один оператор. Для этой цели служит составной оператор. Синтаксически составной оператор может быть указан ключевыми словами begin end (язык Pascal) или фигурными скобками {} (языки C++, Java, Perl).
Структурное программирование
Первоначально все разрабатываемые универсальные языки программирования имели оператор безусловного перехода goto. В настоящее время разработчики языков большей частью придерживаются парадигмы структурного программирования (программирования без goto).К основным достоинствам структурного программирования следует отнести:
Алгоритм выполнения структурированной программы может быть представлен в виде блок-схемы. Такая блок-схема может содержать три типа узлов:
Изображение этих узлов представлено на следующей схеме.

Рис. 3.2. Типы узлов и соответствующие блок-схемы
Правильная структурированная программа представляется блок-схемой, в которой существует только одна входящая и одна исходящая дуга, в любой узел можно попасть от входящей дуги и из любого узла можно попасть к выходящей дуге.
Управляющие структуры
Управление последовательностью действий в языках программирования может быть представлено некоторой управляющей структурой. Такая структура называется неявным управлением в том случае, если последовательность действий определяется естественным образом (например, выполнение программы идет с первого оператора и т.д.). Управляющая структура представляет собой явное управление в том случае, если для изменения порядка выполнения действий используются какие-либо операторы или иные синтаксические конструкции. Основными управляющими структурами принято считать:В языках логического программирования управляющие структуры могут представляться несколько иначе. Так, в языке Prolog модель управления последовательностью действий вместо операторов использует такие категории, как факты, правила и запросы.
Введение в программирование
Определение и активация подпрограмм
Программу можно рассматривать как некоторую иерархическую структуру, состоящую из одной главной программы и множества произвольным образом вызываемых подпрограмм. Точка вызова подпрограммы является и точкой возврата из подпрограммы.Для выполнения любого выражения транслятор преобразует его в некоторый код, который или может быть сразу аппаратно интерпретируем (машинный язык) или программно интерпретируем (использование интерпретатора).
Для выполнения подпрограммы производится ее активация, в результате чего создаются:
Сегмент кода представляет собой неизменяемую часть, статически сохраняемую в памяти, а сегмент данных создается заново при каждом выполнении подпрограммы.
Каждая активация подпрограммы использует один единожды созданный сегмент кода.
Подпрограмма может быть реализована как процедура или функция. Процедура - это подпрограмма, выполняющая определенные действия и завершающаяся без возврата значения определенного типа.
Результатом выполнения функции всегда является возврат некоторого значения.
При выполнении программы текущая выполняемая команда идентифицируется двумя указателями:
При выполнении программы интерпретатор выбирает команду по CIP-указателю, увеличивает значение этого указателя и выполняет выбранную команду. Если при выполнении команды был выполнен безусловный переход или вызов подпрограммы, то значение CIP-указателя опять изменяется. Значение переменной, используемой в подпрограмме, определяется по CEP-указателю, идентифицирующему конкретную запись активации.
Последовательный вызов подпрограмм
Запись активации для главной программы создается или перед началом выполнения программы или в процессе трансляции одновременно с формированием сегмента кода.Перед переходом на подпрограмму текущие значения CIP- и CEP-указателей сохраняются. Выполнение программы начинается с присваивания CEP-указателю адреса записи активации, а CIP-указателю - ссылки на первую команду сегмента кода главной программы. При каждом вызове подпрограммы создается новая запись активации этой подпрограммы, и значение CEP-указателя устанавливается на эту запись активации, а значение CIP-указателя - на первую команду фрагмента кода подпрограммы.
При возврате из подпрограммы восстанавливаются сохраненные значения CIP- и CEP-указателей и удаляется запись активации.
Место сохранения значений CIP-указателя и CEP-указателя иногда называют точкой возврата. Точка возврата представляет собой системный объект, сохраняемый, как правило, в записи активации.
При последовательном вызове подпрограммы (реализуемом иногда как копирование подпрограммы) в процессе выполнения может создаваться множество записей активации подпрограммы, но в каждый отдельный момент времени хранится и используется только одна запись активации, т.е. перед созданием новой записи активации прежняя запись активации должна быть удалена.
Иногда в реализациях для достижения наибольшего быстродействия жертвуют некоторым количеством требуемой памяти: место под запись активации выделяется статически при компиляции (в фрагменте кода) и при каждом вызове происходит только повторная инициализация записи активации. В этом случае никакая подпрограмма не может одновременно иметь более одной записи активации, что значительно сужает возможности программиста. Такая модель выполнения присуща большинству трансляторов языка FORTRAN.
Для аппаратной реализации вызова подпрограммы с заранее фиксированной записью активации достаточно только CIP-указателя, сохраняемого командой перехода с возвратом.
Если место для записи активации должно выделяться динамически, то это, как правило, реализуется с помощью стека.
Стек создается в свободной области памяти. При вызове подпрограммы созданная запись активации помещается в стек. При этом значение указателя стека увеличивается с учетом размера выделяемого пространства. При удалении записи активации значение указателя стека уменьшается с учетом освобождаемого пространства. Выделение и освобождение памяти в стеке происходит с одного конца и фиксируется указателем стека.
Такая реализация позволяет создавать для одной подпрограммы несколько записей активации, которые будут последовательно заноситься в стек.
На рисунке 4.1 представлена организация памяти, используемая при выполнении программы на языке С.

Рис. 4.1. Организация памяти, используемая при выполнении программы на языке С.
Вся память программы делится на статическую, определяемую при трансляции, и динамическую, содержание которой формируется в процессе выполнения. Стек располагается в динамической области памяти.
Также в динамической области памяти располагается куча, пространство которой используется под динамически создаваемые объекты.
Рекурсивный вызов подпрограмм
Применение стека для хранения записей активации позволяет реализовывать не только последовательный вызов подпрограмм, но и рекурсивный вызов.Рекурсивным вызовом подпрограммы называется вызов подпрограммы из самой себя. При n вызовах подпрограммы A в стек будет последовательно добавлено n записей активации этой подпрограммы. Последний вызов подпрограммы A будет завершен первым, и его запись активации будет первой удалена из стека.
Примером использования рекурсии может служить программа вычисления факториала n! =(n*(n-1)!), 0!=1.
Пример подпрограммы на языке С:
int factoria(int n) { if (n) return n* factoria(n-1); else return 1; }
В некоторых случаях без применения рекурсии задачу решить практически невозможно. Хорошо известен пример программирования алгоритма ханойских башен. Условия задачи состоят в следующем: есть три башни, первая башня состоит из колец, диаметр которых увеличивается сверху вниз. Задача заключается в программировании алгоритма, обеспечивающего перемещение колец с первой башни на вторую по одному с возможным использованием вспомогательной третьей башни таким образом, чтобы все кольца оказались на второй башне и их диаметр увеличивается сверху вниз. Алгоритм решения этой задачи посредством рекурсии состоит в предположении, что эта задача решена для n-1 кольца. Тогда, если при n=1 решение задачи очевидно, то есть решение и для n колец.
Пример подпрограммы на языке С++, реализующей алгоритм ханойских башен:
#include "stdafx.h" #include
void tower_3(int n, int m, int k){ /* n - число перемещаемых колец m - башня, с которой выполняется перемещение k - башня, на которую выполняется перемещение */
if (n==1) { //Последнее очевидное перемещение return ;}
tower_3(n-1,m,6-m-k); /* 6-m-k определяет номер вспомогательной башни, m - номер башни, с которой выполняется перемещение */
std::cout<<" from "<< m << " to "<< k << " n= "<

Рис. 4.2. Результат рекурсивного вызова процедуры.
Одним из первых языков программирования, допускавших рекурсию, являлся ALGOL 60. Раньше принципы реализации записей активации трансляторами языка FORTRAN не позволяли применять рекурсию, но последняя версия компилятора языка FORTRAN 90 это допускает.
Подпрограммы A и B называются взаимно рекурсивными, если подпрограмма A вызывает B, а подпрограмма B вызывает A.
Языки программирования, для которых традиционно используются однопроходные компиляторы, для реализации взаимной рекурсии должны вводить предварительное определение подпрограммы. Это объясняется тем, что при взаимно рекурсивном вызове подпрограмм A и B, в момент вызова B из A подпрограмма B должна быть уже определена, а в момент вызова A из B подпрограмма A должна быть определена. Такой механизм используется в языках Pascal и Ada.
Язык Pascal не требует указания сигнатуры функции или процедуры в том случае, если ее определение расположено в коде программы до ее вызова. В противном случае используется предварительное определение, указываемое ключевым словом forward.
На рисунке 4.3 приведен код модуля на языке Pascal (созданного в среде Delphi), иллюстрирующий предварительное объявление функции A.

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

Введение в программирование
Блочно-структурированные языки программирования
В блочно-структурированных языках программирования программа записывается как множество иерархически вложенных блоков определенной структуры.Блочно-структурированные языки программирования можно подразделить на строго блочно-структурированные языки и просто блочно-структурированные языки.
В строго блочно-структурированных языках в начале каждого блока располагается область объявлений, а за ней следует фрагмент кода. Иначе говоря, в строго блочно-структурированных языках программирования не допускается объявление переменных в любом месте фрагмента кода.
К строго блочно-структурированным языкам программирования относятся языки ALGOL 60, Pascal, PL/I.
Каждый блок в блочно-структурированном языке программирования вводит новую среду локальных ссылок.
Следующий пример иллюстрирует блочную структуру программы на языке Pascal.
program Project1; procedure P1(); procedure P2(); {Вложенная процедура} var i:Integer; begin end; begin {Код процедуры P1} end; begin {Код главной программы Project1} end.
В языке Object Pascal по умолчанию приложения создаются как набор модулей, подключаемых ключевым словом uses. Следующий пример иллюстрирует блочную структуру программы на языке Object Pascal, представленную в виде одного модуля.
program Project2; {Начало программы} uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; {$R *.res} {$R *.dfm} {Имя DFM-файла должно совпадать с именем модуля (блока). } {Для получения единого модуля на языке Object Pascal при автоматическом создании приложения в среде Delphi файл Unit1.dfm следует переименовать в Project2.dfm, а код модуля Unit1.pas перенести в модуль Project2.pas}
type {Объявление нового типа окна формы TForm1 } TForm1 = class(TForm) Button1: TButton; Button2: TButton; ListBox1: TListBox; Edit1: TEdit; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure ListBox1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var {Начало области объявлений } Form1: TForm1; i: Integer; procedure TForm1.Button1Click(Sender: TObject); begin Edit1.Text:='Button1'; end; procedure TForm1.Button2Click(Sender: TObject); var i:Integer; procedure P1(); {Вложенная процедура} var i:Integer; begin i:=5; Edit1.Text:= Edit1.Text+' i= ' + IntToStr(i); end; begin Edit1.Text:='Button2'; i:=0; P1 (); end; procedure TForm1.ListBox1Click(Sender: TObject); begin Edit1.Text:='ListBox1'; end; begin {Начало выполнения программы} Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end.
Пример 5.1. Блочная структура программы на языке Object Pascal
На рисунке 5.1 отображен результат выполнения приведенной выше программы (сделан щелчок мышью на кнопке Button2, инициирующий выполнение процедуры TForm1.Button2Click и вложенной процедуры P1).

Рис. 5.1. Окно формы
К простым блочно-структурированным языкам относятся такие языки, как C и Java, позволяющие формировать область объявления переменных в любом месте блока.
Современные языки программирования, такие как C++, Java, Object Pascal, относятся к блочно-структурированным языкам программирования, и при этом программы на этих языках могут состоять из нескольких блоков (программных модулей), расположенных на верхнем уровне иерархии.
Блочная структура организации программ делает "прозрачной" для программиста статическую область видимости, определяя правила объявления идентификаторов. Так, если во вложенном блоке объявляется идентификатор с уже существующим именем, то это "перекрывает" ссылку на одноименный идентификатор во внешнем блоке (без квалификации внешнего идентификатора, когда это позволяет синтаксис языка программирования). Все идентификаторы, объявленные во вложенном блоке, недоступны во внешнем блоке.
Блок может представлять собой:
Начало программы} uses Windows, Messages,
|
program Project2; { Начало программы} uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; {$R *.res} {$R *.dfm} {Имя DFM-файла должно совпадать с именем модуля (блока). } {Для получения единого модуля на языке Object Pascal при автоматическом создании приложения в среде Delphi файл Unit1.dfm следует переименовать в Project2.dfm, а код модуля Unit1.pas перенести в модуль Project2.pas} type {Объявление нового типа окна формы TForm1 } TForm1 = class(TForm) Button1: TButton; Button2: TButton; ListBox1: TListBox; Edit1: TEdit; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure ListBox1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var {Начало области объявлений } Form1: TForm1; i: Integer; procedure TForm1.Button1Click(Sender: TObject); begin Edit1.Text:='Button1'; end; procedure TForm1.Button2Click(Sender: TObject); var i:Integer; procedure P1(); {Вложенная процедура} var i:Integer; begin i:=5; Edit1.Text:= Edit1.Text+' i= ' + IntToStr(i); end; begin Edit1.Text:='Button2'; i:=0; P1 (); end; procedure TForm1.ListBox1Click(Sender: TObject); begin Edit1.Text:='ListBox1'; end; begin {Начало выполнения программы} Application.Initialize; Application.CreateForm(TForm1, Form1); Application.Run; end. |
| Пример 5.1. Блочная структура программы на языке Object Pascal |
| Закрыть окно |
program Project2; {Начало программы}
uses
Windows, Messages, SysUtils, Variants,
Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
{$R *.res}
{$R *.dfm} {Имя DFM-файла должно совпадать
с именем модуля (блока). }
{ Для получения единого модуля на языке Object Pascal
при автоматическом создании приложения в среде Delphi
файл Unit1.dfm следует переименовать в Project2.dfm,
а код модуля Unit1.pas перенести в модуль Project2.pas}
type {Объявление нового типа окна формы TForm1 }
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
ListBox1: TListBox;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure ListBox1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var {Начало области объявлений }
Form1: TForm1;
i: Integer;
procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text:='Button1';
end;
procedure TForm1.Button2Click(Sender: TObject);
var i:Integer;
procedure P1(); {Вложенная процедура}
var i:Integer;
begin
i:=5;
Edit1.Text:= Edit1.Text+' i= ' + IntToStr(i);
end;
begin
Edit1.Text:='Button2';
i:=0;
P1 ();
end;
procedure TForm1.ListBox1Click(Sender: TObject);
begin
Edit1.Text:='ListBox1';
end;
begin {Начало выполнения программы}
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Функции и процедуры
Как уже было сказано в предыдущей лекции, подпрограмма в большинстве языков программирования может быть реализована двумя способами: как функция и как процедура.Если подпрограмма реализуется как функция, то она при завершении должна возвращать значение определенного типа (например, return 0;). Подпрограмма-функция может указываться в выражениях.
Подпрограмма-функция также может быть передана подпрограмме в качестве фактического параметра, например,
(procedure P1 (i: integer; function P2(j:integer):integer);).
Помимо использования глобальных переменных, некоторые языки программирования позволяют делать локальные переменные подпрограмм доступными для других подпрограмм, выполняющихся вне текущей записи активации данной подпрограммы. Это может быть реализовано как экспорт данных (externs в языке С, uses в языке Pascal), и в таком случае экспортируемая локальная переменная должна быть доступна вне записи активации. Поэтому ее, как правило, размещают в сегменте кода подпрограммы.
К способам реализации совместного использования данных относится и механизм явно организуемой общей среды. Все объекты данных, которые предполагается использовать в ряде подпрограмм, могут быть помещены в такую общую среду. Этот механизм реализуется в таких языках программирования, как С (переменные помечаются как extern), Ada (переменные указываются в пакете), FORTRAN (переменные задаются в блоке COMMON). В объектно-ориентированных языках программирования совместное использование данных может быть реализовано через модификаторы доступа переменных членов классов.
Другие языки программирования (LISP, APL) для реализации совместного использования данных применяют механизм динамической области видимости, при котором выполняется восходящий поиск ассоциации для переменной (ассоциация для нелокальной переменной подпрограммы последовательно ищется во всех средах ссылок выполняемых ранее подпрограмм).

Передача параметров
Внешние данные в подпрограмме могут использоваться, если они являются глобальными объектами для данной подпрограммы или переданы ей в качестве параметров. В описании подпрограммы указываются формальные параметры, а в точке вызова подпрограммы задаются фактические параметры. Формальный параметр определяет тип передаваемого фактического параметра. Фактический параметр представляет собой выражение соответствующего типа.Различаются следующие способы передачи параметров: по ссылке, по имени, по значению, по значению-результату, по результату, по значению-константе. Набор допустимых способов передачи параметров зависит от конкретного языка программирования.
При передаче параметров по ссылке в записи активации, создаваемой при выполнении подпрограммы, ассоциация для фактического параметра может формироваться в нелокальной среде ссылок (в зависимости от реализации компилятора). Так, при вызове подпрограммы для параметров, передаваемых по ссылке, формируются указатели, доступные как из вызывающей, так и из вызываемой подпрограммы. Таким образом, при изменении в вызываемой подпрограмме значения фактического параметра, переданного по ссылке, в вызывающей программе объект, соответствующий фактическому параметру, также будет изменен.
При передаче параметров по имени в вызываемой подпрограмме до ее выполнения происходит замена формальных параметров на фактические. При этом значения фактических параметров вычисляются внутри вызываемой подпрограммы.
При передаче параметров по значению в вызываемой подпрограмме создается псевдоним фактического параметра (копия фактического параметра), значение которого присваивается формальному параметру подпрограммы. При таком способе передачи параметров изменения, произведенные в вызванной процедуре над значением фактического параметра, будут не видны далее в вызывающей подпрограмме.
При передаче параметров по значению-результату измененное значение фактического параметра возвращается вызывающей подпрограмме. Однако, в отличие от передачи параметров по ссылке, при данном способе сначала в момент вызова подпрограммы значение фактического параметра присваивается соответствующему формальному параметру вызываемой подпрограммы, и все изменения выполняются над формальным параметром, а не над ссылкой на фактический параметр.
При завершении вызываемой подпрограммы текущее значение формального параметра присваивается обратно фактическому параметру. Такой тип передачи параметров был применен в языке ALGOL-W.
При передаче параметров по результату текущее значение формального параметра присваивается обратно фактическому параметру при завершении вызываемой подпрограммы, но первоначальное значение фактического параметра вызываемой подпрограмме не передается. Формальный параметр подпрограммы должен быть инициирован подпрограммой (реализация может позволять инициализацию по умолчанию в соответствии с типом данных параметра).
При передаче параметров по значению-константе значение фактического параметра не может быть изменено. Такой способ передачи параметров может быть реализован и как частный случай передачи параметра по значению, и как передача параметра по ссылке на константное выражение, что однозначно гарантирует неизменность фактического параметра вызывающей подпрограммы.
В большинстве случаев языки программирования используют один или два способа передачи параметров. Так, язык программирования С позволяет только передачу параметров по значению (ссылка реализуется как значение указателя). В языке программирования Pascal допускается два способа передачи параметров: по значению и по ссылке.
В следующей таблице приведены фрагменты кода, иллюстрирующие способы передачи параметров в языках программирования С и Pascal.
| Передача параметров по значению | procedure P1(i: integer); begin i:=0; end; | // Передача значения void P1(int i) {i=0; } //Вызов подпрограммы i=10; P1(i); |
| Передача параметров по ссылке | procedure P1(var i: integer); begin i:=0; end; | // Передача указателя на значение void P1(int *i) { *i=*i+1;} //Вызов подпрограммы i=10; P1(&i); |
Управление данными
При интерпретации выполняемого выражения, содержащего данные, компилятор должен однозначно "понимать" указываемые идентификаторами операнды выражения. Так, один и тот же идентификатор может в различных частях программы использоваться для обозначения разных данных. Идентификатор может быть меткой или именем подпрограммы. В первом случае при трансляции выражения компилятор должен инициировать сообщение об ошибке компиляции. В зависимости от того, указывает ли идентификатор на переменную или на константу компилятор также должен предпринимать различные действия.Имя в программе может быть простым или составным. К простым именам относятся имена переменных, имена подпрограмм, имена пользовательских типов данных. Составное имя ссылается на элемент некоторой структуры и записывается как простое имя и следующие за ним операции квалификации или индексации имени. Например, выражение array1[3] ссылается на третий элемент массива, а выражение record1.field2 ссылается на значение поля field2 структуры record1.
Для управления данными конкретные идентификаторы подпрограммы должны быть сопоставлены конкретным объектам данных. Такое сопоставление иногда называется ассоциацией. В момент создания записи активации устанавливается множество ассоциаций, сопоставляющих идентификаторы с текущими объектами данных. Такое множество ассоциаций определяет среду ссылок подпрограммы.
Среда ссылок подпрограммы включает среду глобальных ссылок, среду локальных ссылок, среду нелокальных ссылок и среду предопределенных ссылок.
Среда локальных ссылок образуется локальными переменными, формальными параметрами, а также подпрограммами, определенными внутри текущей подпрограммы.
Среда глобальных ссылок формируется ассоциациями, которые созданы при активации главной программы. Среда нелокальных ссылок содержит как среду глобальных ссылок, так и множество ассоциаций, сформированных для доступных далее в программе, но еще не созданных переменных.
Область видимости ассоциации в подпрограмме определяется включением этой ассоциации в среду ссылок подпрограммы.
При этом если имело место переопределение глобального идентификатора локальным идентификатором, то ассоциация для глобального идентификатора исключаются из среды глобальных ссылок подпрограммы.
Динамическая область видимости каждой ассоциации определяется совокупностью записей активации подпрограмм, включающих данную ассоциацию в среду ссылок подпрограммы.
На один и тот же объект данных можно ссылаться посредством различных имен - например, если в подпрограмме доступен идентификатор глобальной переменной и эта же переменная передается по ссылке как формальный параметр подпрограммы. Различные идентификаторы, существующие в среде ссылок для ассоциации с одним и тем же объектом данных, иногда называются псевдонимами.
Использование псевдонимов может приводить к различным ошибочным ситуациям. К тому же, наличие в подпрограмме псевдонимов осложняет процесс оптимизации кода, выполняемый компилятором. Например, если при выполнении последовательности двух операторов
int1=5*x; int2=8*y;
значение переменной int1 нигде в программе не используется, то при оптимизации программы первый оператор может быть упразднен. Но если переменная int1 является псевдонимом переменной y, то такая оптимизация приведет к ошибке вычисления, и, следовательно, потребуются дополнительные проверки.
Под статической областью видимости понимается фрагмент кода программы, в котором идентификатор ссылается на конкретный объект.
Статическая область видимости определяет объект, на который ссылается идентификатор в коде программы, а динамическая область видимости формируется ассоциациями, созданными во время выполнения программы.
Введение в программирование
Константы в языке Object Pascal
Объявление переменных выполняется в секции, начинающейся с ключевого слова var. Ключевое слово const предназначено для объявления констант.Константами могут быть простые значения или значения типа записи, массива, функции и указателя.
При объявлении константного указателя его сразу необходимо инициализировать.
Инициализация константного указателя может быть выполнена тремя способами:
Например:
const PI: ^Integer = @i1; { i1 - переменная типа Integer} const StrNew: PChar = 'NewConst'; const PFunc: Pointer = @MyFunc;
Объявление объектов данных
Перед использованием в программе любой объект данных должен быть объявлен. Однако некоторые языки допускают неявное объявление. Так, в языке FORTRAN компилятор определяет тип используемой, но предварительно не объявленной переменной по первому символу имени, а в языке Perl неявное объявление переменной происходит при присваивании ей начального значения. Некоторые языки выполняют неявное объявление переменных, используемых в качестве счетчиков циклов.Оператор объявления сообщает компилятору информацию об идентификаторах и типах данных, назначаемых объектам данных, и о предполагаемом времени жизни этих объектов (глобальная или локальная переменная, переменная - член класса, статическая переменная и т.п.).
При объявлении объекта данных в зависимости от типа этого объекта и языка программирования возможно выделение памяти под этот объект при объявлении или при последующем создании этого объекта.
В языке C++ для объявления новых имен в текущей области видимости предназначаются операторы объявления.
Например: int i, j; float m, n;
Оператор объявления в языке C++ может указываться в любом допустимом месте программы.
В языке C++ каждый оператор объявления завершается символом конца оператора (точка с запятой). В операторе объявления может объявляться несколько объектов данных одного типа, перечисляемых через запятую. Любой оператор объявления начинается с ключевого слова или идентификатора, указывающего тип объявляемого объекта.
Объявляемыми объектами данных могут быть названия типов и имена объектов.
Объявление определяет имя внутри области видимости. Имя, объявляемое внутри блока, класса, функции или пространства имен, является локальным. Блок в языке C++ заключается в фигурные скобки. Если вне блока существуют глобальные имена, обозначаемые теми же идентификаторами, то они становятся скрыты внутри блока и к ним следует обращаться, используя оператор разрешения области видимости ::.
Например:
int i1; // Объявление глобальной // переменной void metod1() { int i1; // Объявление локальной // переменной i1=22; // Доступ к локальной // переменной метода ::i1=44; // Доступ к глобальной // переменной { int i1; // Объявление локальной // переменной i1=33; // Доступ к локальной // переменной блока } i1=44; // Доступ к глобальной // переменной }
В языке C++ объявления применяются для создания нового имени, используемого в приложении (имя может указывать не только объект данных). Объявления позволяют:
Отметим, что термин объявление означает только указание, каким образом объявляемое имя будет интерпретироваться компилятором.
В языке C++ при каждом объявлении локального объекта под него выделяется память.
Например:
// Объявление и инициализация // локального объекта в цикле
do { char char1 = _getch(); if( char1 == NULL ) continue; return (char1); } while( 1 );
Удаление объекта, который был объявлен в цикле, происходит после каждой итерации, при выходе из блока или при передаче управления в точку, расположенную выше объявления этого объекта.
Процедура удаления объекта в языке C++ может включать не только удаление объекта из памяти (освобождение памяти), но и выполнение деструкторов (для объектов типа класса).
В языке Java объявления переменной могут указываться в любом допустимом месте программы.
Например:
boolean b1=true; // Оператор присваивания // задается символом = char c1='N'; int int1=123;
Имена могут иметь следующие элементы языка Java:
Если имя или выражение означает переменную или значение простого типа, тогда тип этой переменной или значения называется типом имени или выражения.
В языке Object Pascal оператор объявления может указываться только в области объявлений, определяемой ключевым словом var.
Например:
var ch1: Char; {Оператор объявления в языке Object Pascal} begin ch1 := Chr(65); { Оператор присваивания задается символами :=} end;
Переменные и константы
Будем называть объектом данных один или несколько однотипных элементов данных, рассматриваемых программой как одно простое или составное целое.Объекту данных присущи такие атрибуты, как тип, адрес памяти, присвоенное значение, имя.
Переменные и константы - это объекты данных. При создании переменной или константы им назначается имя, называемое иногда идентификатором. В отличие от переменной, атрибут "значение" объекта данных "константа" не может быть изменен в процессе выполнения программы.
Представление целых и вещественных типов данных
Представление целочисленных и вещественных значений в памяти компьютера в большинстве случаев реализуется аппаратным способом с учетом возможностей конкретного процессора. Также аппаратно реализуется примитивный набор операций над этими значениями. Применение операций, реализуемых аппаратно, значительно более эффективно, чем использование программно реализуемых операций. Поэтому компиляторы по возможности формируют код, в котором применяется аппаратная реализация примитивных операций (таких, как сложение, вычитание, умножение и деление).Целочисленное значение типа integer, записанное как "signed 32-bit", может иметь в памяти компьютера следующее представление:

Рис. 6.1. Битовая структура типа integer
Целочисленное значение типа shortint, записанное как " signed 8-bit ", может иметь в памяти компьютера следующее представление:

Рис. 6.2. Битовая структура типа shortint
Максимально допустимое значение, которое можно записать в 7 бит - это восьмеричное число 177, которое в десятичном исчислении равно 127 (1778=1*82+7*81+7*80=12710).
Таким образом, диапазон допустимых значений для каждого целочисленного типа данных определяется как стандартом языка, так и возможностями аппаратуры.
В некоторых языках программирования представление целочисленного значения включает еще и хранение дескриптора этого значения. При этом дескриптор может храниться как в одной ячейке со значением, так и в различных ячейках. В первом случае наличие дескриптора значительно уменьшает диапазон допустимых значений, а также, как правило, замедляет процесс выполнения арифметических операций (операции над частью ячейки памяти не поддерживаются аппаратно).
При хранении дескриптора и значения в разных ячейках (этот способ представления используется для языка LISP) одновременно с дескриптором хранится и указатель физического расположения значения.
Вещественные типы аппаратно могут иметь два представления: вещественные числа с фиксированной точкой и вещественные числа с плавающей точкой. Как правило, по умолчанию компиляторы преобразуют вещественные значения в экспоненциальный формат (формат с плавающей точкой), если синтаксис языка явно не указывает применение формата с фиксированной точкой.
Вещественное значение с плавающей точкой может иметь в памяти компьютера следующее представление:

Рис. 6.3. Битовая структура типа float
Для представления вещественных чисел с плавающей точкой и вещественных чисел двойной точности с плавающей точкой стандартом IEEE 754 определены 32- и 64-битовые представления соответственно.
В разряды мантиссы записываются значащие цифры, а в разряды экспоненты заносится показатель степени. Положительные значения содержат в знаковом бите числа - 0, а отрицательные значения - 1.
При обозначении чисел с плавающей точкой приняты следующие соглашения: знаковый разряд обозначается буквой s, экспонента - e, а мантисса - m.
В языке Java значения с плавающей точкой могут объявляться следующими базовыми типами:
Полной ненулевой формой значения типа float является форма s·m·2e, где s это +1 или -1 (знаковый бит числа), m является положительным значением, меньшим, чем 224 (мантисса), и e является целым в пределах от -149 до 104 включительно (экспонента).
Полной ненулевой формой значения типа double является форма s·m·2e, где s это +1 или -1, m является положительным значением, меньшим, чем 253, и e является целым в пределах от -1045 до 1000 включительно.
Положительным нулевым значением и отрицательным нулевым значением являются 0.0 и -0.0 (0.0 == -0.0 дает в результате true), но некоторые операторы различают эти "эквивалентные" значения, например, 1.0/0.0 равно положительной бесконечности, а 1.01/-0.0 - отрицательной бесконечности.
В языке Java приняты следующие правила приведения вещественных типов:
Если оба операнда имеют тип float, или один имеет тип float, а другой - целый тип, то результат будет иметь тип float.
Если же хотя бы один из операндов имеет тип double (устанавливаемый по умолчанию), то другой операнд также приводится к типу double и результат будет иметь тип double.
Операции со значениями с плавающей точкой не выдают исключений.
При вычислениях значений с плавающей точкой следует учитывать правила округления. Так например, для значений с плавающей точкой выражение1.0*10 не равно значению 10. Это иллюстрирует следующий пример на языке Java:
public class MyFloatSumma { public static void main(String[] args) { float f1 = (float) 1.0; // Число с плавающей точкой float fSum = 0; // Переменная для записи результата final static float myDeltaFloat = (float) 0.00001; // Дельта для "усечения" // мусора мантиссы
// Способ вычисления - простое // суммирование и сравнение for (byte i=0; i<10; i++) fSum = fSum + (float) 0.1; // Цикл от 0 до 10
if (f1 == fSum) // Сравнение (float) 1.0 и // полученной суммы System.out.println("Числа равны"); else System.out.println("Числа различны"); // Правильный способ вычисления - // с "усечением" мусора мантиссы fSum = 0; for (byte i=0; i<10; i++) fSum = fSum + (float) 0.1; if (Math.abs(fSum - f2) < myDeltaFloat) System.out. println("Числа одинаковы"); else System.out. println("Числа различны"); } }

Преобразование типов в языке C++
Язык C++ позволяет выполнять преобразование значения одного типа в значение другого типа. Преобразование типа может быть явным и неявным. Явное преобразование называется приведением типов.Для явного приведения типов можно использовать две формы записи:
без указания дополнительной информации для преобразования типа (перед преобразуемой переменной в скобках указывается имя типа, к которому она приводится).
Например:
std::cout <<"strlen="<<(long)strlen(pArr1);
с указанием дополнительной информации для преобразования типа (используются операторы static_cast, dynamic_cast, reinterpret_cast и const_cast).
Например:
std::cout <<"strlen="<
Вторая форма явного преобразования типов более предпочтительна, поскольку несет в себе дополнительную информацию, позволяющую сократить число возможных ошибок.
Дополнительная информация о приводимом типе может быть указана следующими операторами:
Преобразование типов выполняется для значений переменных при вычислении выражений и оказывает влияние на тип результата. Преобразование типов не изменяет типа самих переменных, участвующих в выражении.
Типы данных языка C++
Тип данных определяет размер памяти, выделяемой под переменную данного типа при ее создании.C++ поддерживает следующие типы данных:
Базовые типы языка C++ можно подразделить на две группы: целочисленные и вещественные. Существует еще ключевое слово void, которое используется для описания типа подпрограмм, не имеющих возвращаемого значения, или при указании параметров подпрограмм неизвестного типа.
char signed char | Символьный тип. Диапазон значений от -128 до 127. Содержит один символ или строку символов. Каждый символ представляется одним байтом. Компилятор различает как отдельные следующие типы: char, signed char и unsigned char. | ||
| unsigned char | Символьный тип. Диапазон значений от 0 до 255. Каждый символ представляется одним байтом (значение в диапазоне от 0 до 255). | ||
short signed short | Целый тип. Диапазон значений от -32768 до 32767. Сокращенное обозначение типа short int. Длина этого типа вне зависимости от используемого компилятора всегда больше или равна длине значения типа char и меньше или равна длине значения типа int. | ||
| unsigned short | Беззнаковый целый тип. Диапазон значений от 0 до 65535. | ||
int signed int | Целый тип. Диапазон значений от -2147483648 до 2147483647. Длина этого типа вне зависимости от используемого компилятора всегда больше или равна длине значения типа short int. В общем случае размер типа int языка C++ зависит от конкретной реализации. | ||
| unsigned int | Беззнаковый целый тип. Диапазон значений от 0 до 4294967295. | ||
| __intn | Целый тип, размер в битах которого определяется значением n, и может быть равным 8, 16, 32 или 64 битам. | ||
long signed long | Целый тип. Диапазон значений от -2147483648 до 2147483647. Сокращенное обозначение типа long int. | ||
| unsigned long | Беззнаковый целый тип. Диапазон значений от 0 до 4294967259 | ||
| float | Тип данных с плавающей точкой. Диапазон значений от 3.4Е-38 до 3.4Е+38. | ||
| double | Тип данных с плавающей точкой двойной точности. Диапазон значений от 1.7Е-308 до 1.7Е+308. Длина типа double вне зависимости от используемого компилятора всегда больше или равна длине типа float и короче или равна длине типа long double. | ||
| long double | Тип данных с плавающей точкой двойной точности, длина которого равна длине значений типа double. Типы double и long double имеют одинаковое представление, но компилятор трактует их как различные типы. |
Размер памяти, выделяемой под каждый тип данных языка C\C++, в некотором смысле зависит от используемой платформы и компилятора. Однако все компиляторы гарантируют, что для типа short памяти всегда выделяется меньше или столько же, сколько и для типа int, а тип long всегда "длиннее" или равен типу int.
Символьные типы char и unsigned char представляют значение символа, реализуемое одним байтом. Для использования символов в кодировке Unicode язык C++ предоставляет тип wchar_t, в котором под каждый символ отводятся два байта.
Ключевое слово typedef языка C++ позволяет объявить новое имя существующего типа.
Например:
typedef int my_integer; my_integer i1=0;
Создание псевдонима для типа значительно облегчает процесс изменения типа переменных в уже отлаженном коде.
Символьным литералом или символьной константой называется символ, заключенный в одинарные кавычки. Целые литералы представляют целочисленные константы в десятичном, восьмеричном или шестнадцатеричном виде.
Восьмеричные литералы начинаются с символа 0, а шестнадцатеричные - с символов 0x.
После литерала может быть указан суффикс, определяющий его тип: U - unsigned int, L - long int.
Например:
00 // Восьмеричное значение, равное 0 oxffff // Шестнадцатеричное значение, // равное 65535 10L // Значение типа long int
При записи шестнадцатеричных литералов каждый знак может принимать значение от 0 до 15. Для записи значений от 10 до 15 принято использовать следующие символы: a - 10, b - 11, c - 12, d - 13, e - 14 и f - 15.
Литералы с плавающей точкой могут записываться в двух формах: вещественной и экспоненциальной.
Например:
1.53 32.24e-12 // Экспоненциальная форма записи литерала
Литералы с плавающей точкой могут использоваться для инициализации значений переменных вещественного типа (float, double или long double).
Типы данных языка Java
Java - это язык со строгой типизацией. Это означает, что типы данных должны быть определены уже на этапе компиляции.Все типы данных в Java можно подразделить на две группы:
Язык Java позволяет использовать следующие простые типы:
целые типы:
типы значений с плавающей точкой:
символьный тип
логический тип
Например:
// Объявление переменных boolean b1=true; float fN1=74.3F; float fN2=(float)(fN1+2); double dN1=14573.74; double dN2= 81.2E-2;
Значение одного целого типа может быть приведено к значению другого целого типа.
Приведение целых типов к логическому и обратно выполнять нельзя.
Язык Java предусматривает для целочисленных значений следующие операторы:
Если оба операнда имеют целочисленный тип, то операция рассматривается как целочисленная. Если один из операндов имеет тип long, то операция выполняется с использованием 64-битового представления: при этом любой другой операнд будет расширен до типа long и результат будет также иметь тип long. В противном случае операции выполняются, используя 32-битовое представление (любой операнд, не являющийся int, первоначально расширяется до типа int) и результат будет иметь тип int.
Логические значения представляются одним битом и обозначаются константами true и false
В языке Java нельзя прямо выполнить приведение типа int к типу boolean, но можно конвертировать значение int, используя следующий синтаксис языка: int x != 0, где любое не нулевое значение x даст в результате true, а нулевое значение даст false. Для преобразования значения типа boolean в целое следует выполнить условный оператор: bool x ? 1:0.
Для логических значений в языке Java предусмотрены следующие операторы:
Типы данных языка Object Pascal
Все переменные и константы, используемые в программе, всегда принадлежат какому-либо типу. Вызов функции также возвращает значение определенного типа.Типы данных языка Object Pascal можно разбить на следующие группы:
В язык Object Pascal включены следующие базовые типы данных:
Целочисленные типы
| Integer | -2147483648..2147483647 | signed 32-bit |
| Cardinal | 0..4294967295 | unsigned 32-bit |
| Shortint | -128..127 | signed 8-bit |
| Smallint | -32768..32767 | signed 16-bit |
| Longint | -2147483648..2147483647 | signed 32-bit |
| Int64 | -263..263-1 | signed 64-bit |
| Byte | 0..255 | unsigned 8-bit |
| Word | 0..65535 | unsigned 16-bit |
| Longword | 0..4294967295 | unsigned 32-bit |
Действительные типы
| Real | 5.0 * 10-324 .. 1.7 * 10308 | 8 |
| Real48 | 2.9 * 10-39 .. 1.7 * 1038 | 6 |
| Single | 1.5 * 10-45 .. 3.4 * 1038 | 4 |
| Double | 5.0 * 10-324 .. 1.7 * 10308 | 8 |
| Extended | 3.6 * 10-4951 .. 1.1 * 104932 | 10 |
| Comp | -263+1 .. 263 -1 | 8 |
| Currency | -922337203685477.5808.. 922337203685477.5807 | 8 |
Для указания значения действительного типа можно использовать экспоненциальный формат (например, значение 1.3Е-5 эквивалентно 1.3*10-5).
Логические типы
| Boolean | True или False | 1 |
| ByteBool | True или False | 1 |
| WordBool | True или False | 2 |
| LongBool | True или False | 4 |
Символьные типы
| Char | ANSI-символ | 1 |
| AnsiChar | ANSI-символ | 1 |
| WideChar | Unicode-символ | 2 |
Строковые типы
| string | Определяется директивой компилятора $H | |
| ShortString | 255 символов | От 2 до 256 байт |
| AnsiString (длинная строка) | ~231 символов | От 4 байт до 2 Гбайт |
| WideString (Символы Unicode) | ~230 символов | От 4 байт до 2 Гбайт |
Для строковых переменных выполняются следующие правила:
Например:
var S1: string; {Объявление строковой переменной произвольной длины} S2: string[2]; {Объявление строковой переменной длиной 2 символа}
Строки типа AnsiString также называют длинными строками (long string), представляющими динамически размещаемые строки, длина которых ограничена только доступной памятью. Такие строки используют 1-байтовое представление ANSI-символов.
Реально переменная типа AnsiString является указателем, состоящим из 4 байт. Если строка пустая (ее длина равна 0), то указатель равен nil и для хранения строки никакой памяти не выделяется. Если строка не является пустой, то данная переменная указывает на динамически размещаемый блок памяти, содержащий значение строки, на 32-битовое значение длины строки и на 32 битовое значение счетчика ссылок на строку.
Несколько идентификаторов строк могут ссылаться на одну строку. При этом им не будет выделяться дополнительная память, а только будет выполняться увеличение счетчика ссылок.
Типы данных
Тип данных назначается объекту данных при его объявлении и определяет: значения, которые может принимать объект данного типа; операции, которые используются для манипуляции над объектами заданного типа.Современные языки программирования, как правило, могут иметь набор простых типов, являющихся встроенными в данный язык программирования, и средства для создания производных типов.
Объектно-ориентированные языки программирования позволяют определять типы класса.
Реализация простых типов данных заключается в способе представления значений данного типа в компьютере и в наборе операций, поддерживаемых для данного типа.
Введение в программирование
Элементы массива
Доступ к элементам массива может выполняться:Для получения адреса элемента массива применяется оператор &.
Имя массива является адресом массива и эквивалентно следующему выражению: &имя_массива[0].
Для определения размерности массива в байтах можно использовать функцию sizeof(имя_массива).
Например:
arrayOfInteger[0]=1; // Присваивание значения // первому элементу массива iInt1=arrayOfInteger[1]=13; // Групповое присваивание iInt2=&arrayOfInteger[4]; // Получение адреса пятого // элемента массива
Константные указатели
Значение указателя на константу можно изменять, а значение константного указателя является константой и не подлежит изменению.Например:
char str1[]="123"; const char* pstr1= str1; // pstr1 можно изменять, // а *pstr1 - нельзя.
Задание ключевого слова const перед объявлением указателя создает этот указатель как указатель на константу (при этом само значение, доступное не через данный указатель, остается изменяемым).
Для того чтобы создать константный указатель, вместо оператора * используется *const.
Например:
const char *const pstr1= str1;
Объявление массивов
Массив переменных или объектов состоит из определенного числа однотипных данных, называемых элементами массива. Все элементы массива индексируются последовательно, начиная с нуля.Размещение элементов массива в памяти выполняется последовательно.
Количество элементов в массиве определяет размер массива и является константным выражением. Для создания динамических массивов стандартная библиотека C++ предусматривает тип vector.
Имя массива определяет адрес первого элемента массива.
Имя массива в отличие от имени вектора нельзя указывать в левой части оператора присваивания.
Объявление массива может иметь следующее формальное описание:
// Объявление одномерного массива тип имя_массива[размерность_N]; // Объявление одномерного массива с // одновременной инициализацией тип имя_массива[размерность_N]= {значение0, значение1, ..., значение_N-1}; // Объявление безразмерного массива с // одновременной инициализацией: // размерность определяется количеством // значений, указанных в списке инициализации тип имя_массива[]={значение0, значение1, ..., значение_N-1}; // Объявление многомерного массива тип имя_массива[размерность_1N]... [размерность_MN]; // Объявление многомерного массива с // одновременной инициализацией тип имя_массива [размерность_N]... [размерность_M] = { {значение0, значение1, ..., значение_M-1}, ... {значение0N, значение1N, ..., значение_NM-1}};
Размерность массива может:
Например:
int arrayOfInteger[17]; // Массив из 17 переменных типа int char arrayOfChar_3[3]={'L','M','N'}; // Объявление и инициализация // символьного массива char arrayOfChar_0[]={"Array of char. \n"}; int arrayOfInteger_6[2][3]= { {1,2,3}, {11,12,13}}; // Объявление и инициализация // двумерного массива
Если ни размерность массива, ни список значений инициализации не указаны, то будет создан массив нулевой длины.
Объявление многомерных массивов выполняется по следующим правилам:
Инициализацию массива можно выполнить одним из следующих способов:
Если количество значений инициализации больше, чем объявленная размерность массива, то компилятор Visual C++ инициирует ошибку.
Если количество значений инициализации меньше, чем объявленная размерность массива, то оставшиеся значения автоматически инициализируются нулями.
Преобразование типа указателя
Преобразование типа указателя происходит при различных операциях, таких как вызов процедуры, присваивание, инициализация и т.п.Указатель типа void может быть преобразован к указателю любого другого типа только явным приведением типа. Но указатель любого типа может быть неявно преобразован к указателю типа void.
Это применяется для передачи параметров функции, если тип формального параметра не очевиден (он может быть указателем типа int * или типа float *). В этом случае в прототипе функции вместо явного задания типа записывается тип void.
Например:
Fx(void *px); // Прототип функции Fx(pi); // Вызов функции для int * pi Fx(pf); // Вызов функции для float * pf
В теле функции для работы с указателем следует использовать явное преобразование типа.
Например: (int *)px.
Производные типы
Производные типы можно условно подразделить на две группы:Размещение массива в памяти
При создании массива память под все его элементы выделяется последовательно для каждого элемента в зависимости от типа массива. Для многомерных массивов в первую очередь изменяются значения самого правого индекса.Например, для массива char aCh[2][4] будет выделено восемь байтов памяти, в которых в следующем порядке будут размещены элементы массива:
| aCh[0][0] | aCh[0][1] | aCh[0][2] | aCh[0][3] | aCh[1][0] | aCh[1][1] | aCh[1][2] | aCh[1][3] |
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
Двухмерные массивы можно рассматривать как матрицу, в которой первый индекс определяет строку, а второй индекс - столбец. Порядок расположения элементов матрицы в памяти - по строкам.
При размещении трехмерного массива char aCh[3][2][5] память под элементы этого массива будет выделяться последовательно в соответствии со следующими значениями индексов:

Рис. 7.1.
Общий объем выделяемой под массив памяти определяется как произведение всех размерностей массива (общее число элементов), умноженное на длину типа данных массива.
Символьные массивы и строки
Синтаксис языка не имеет простого строкового типа (стандартная библиотека содержит тип string, но переменные данного типа не являются массивами символов). Поэтому для работы со строками удобно использовать массивы символов. Объявить массив символов - строку можно двумя способами:Символом конца строки служит null-символ \0. При подсчете числа символов в строке учитывается общее число символов плюс символ конца строки.
Например:
char arrayOfChar[6]; arrayOfChar[0]='A'; // Доступ через указатель // на массив и индекс char * ph = arrayOfChar; // Создание указателя на элемент массива ph++; // Переход к следующему элементу массива
Префиксный оператор * называется оператором разыменования. Если ph указывает на элемент массива, то *ph является значением этого элемента.
Любое выражение, имеющее тип массива, может быть преобразовано к указателю того же типа: результатом будет указатель на первый элемент массива.
Например:
char cStr[_MAX_PATH]; // Массив типа char char *pStr = cStr; // Указатель на массив: // эквивалентно выражению &cStr[0]
Ссылки
Ссылка вводит для доступа к переменной второе имя и является константным указателем на объект. Значение переменной-ссылки изменить нельзя. При объявлении ссылки перед именем переменной ставится символ &. Ссылка при объявлении всегда должна быть проинициализирована.Например:
int iV=12; int &sV=iV; // sV - ссылка cout<< sV; // sV равно 12 sV=22; // значение iV // стало равно 22 sV++; // sV и iV стало равно 23 int *pV= &sV; // указатель на sV и iV
Типы, определяемые в пространствах имен
Пространство имен позволяет именовать группу переменных и методов.Создание пространства имен указывается ключевым словом namespace.
Пример:
namespace S // Пространство имен S { int i; } void main() { S::i++; // Обращение к переменной // i из пространства имен S }
В языке C++ объявляемые пространства имен могут быть иерархически вложены друг в друга.
Например:
namespace Outer { int iOuter1= 111; int func(int j); namespace Inner { int iInner1 = 222; } }
Для традиционных приложений можно использовать стандартную библиотеку C++, которая определяет дополнительный набор типов. Пространство имен стандартной библиотеки обозначается идентификатором std.
Для того чтобы иметь возможность обращаться к переменным или методам из пространства имен, можно использовать один из следующих способов:
имя соответствующей переменной или метода должно быть квалифицировано названием пространства имен (пространство имен указывается перед именем через два символа двоеточия). Например:
std::string s="Это строка";
имя библиотеки должно быть установлено как доступное оператором using. Например:
using namespace std; // ... string s1="Строка s1";
Оператор using можно указывать как до метода main, так и внутри метода main (в этом случае переменные и методы пространства имен будут доступны без квалификации их имени сразу после выполнения оператора using).
Для управляемых расширений используются библиотеки среды NET Framework, реализованные как пространства имен. Пространство имен System предоставляет большой набор типов, реализованных как классы или структуры.

Указатели на массивы
И указатель, и массив содержат адрес. Поэтому указателю может быть присвоен адрес первого элемента массива или имя массива. Но адресу массива нельзя присвоить значение указателя.Например:
float fArray[3]; // Массив float* pArray; pArray=fArray; // Эквивалентно оператору // pArray=&fArray[0]; pArray++; // Указывает на второй // элемент массива float* pArray2; pArray2=&fArray[1]; // Указывает на второй //элемент массива
Указатели на переменные
Указатель на переменную содержит адрес памяти расположения этой переменной.Объявление указателя имеет следующее формальное описание:
тип_переменной *имя_переменной_адреса;
Инициализация указателя выполняется следующим образом:
тип_переменной имя_переменной_содержания; имя_переменной_адреса = &имя_переменной_содержания;
Объявление указателя может быть выполнено с одновременной инициализацией:
тип_переменной *имя_переменной_адреса = &имя_переменной_содержания;
Доступ к значению переменной по указателю имеет следующее формальное описание:
имя_переменной_содержания1=*имя_переменной_адреса;
При работе с указателями действуют следующие правила:
Операцию разыменования & нельзя использовать:
Например:
int iVar; int* pInt; // Указатель pInt=&iVar; // Эквивалентно оператору // int *pInt=&iVar; *pInt=20; // Эквивалентно оператору // iVar=20; char* pStr="The string"; iVar=strlen(pStr); // Определение длины строки
Указателю может быть присвоено значение другого указателя: в этом случае следует использовать операцию *. Компилятор Visual C++ отслеживает некорректные присваивания - указателю значения или наоборот, и выдает предупреждение о различных уровнях адресации.
На следующей схеме иллюстрируется соотношение указателя и значения.
| Адреса ячеек памяти | 2000 | 2001 | 3000 | 3020 | ||||
| Операции: | Содержание ячеек: | |||||||
| A='w'; | w | |||||||
| pA=&A; //адрес переменной А | 2000 | |||||||
| B=*pA; //значение переменной А | w | |||||||
| *pA='я'; //изменение значения | я | |||||||
| pB=pA; //адрес переменной А | 2000 | |||||||
| pB=&A; //адрес переменной А | 2000 | |||||||
Выполнять вычитание можно только над указателями одного типа. Результатом вычитания указателей является целое число, указывающее разность адресов памяти.
Над указателями одного типа можно выполнять операцию сравнения, возвращающую логическое значение.
Так же как и при работе с массивами, компилятор Visual C++ не выполняет для указателей проверку на предельные значения.
Указатели на указатели
Объявление указателя на указатель имеет следующее формальное описание:тип **имя_указателя_на_указатель;
При объявлении указателя на указатели уровнем вложенности указателей служит число звездочек перед именем переменной.
Для получения значения по указателю на указатели перед именем указателя надо записать количество звездочек, равное уровню вложенности указателя.
Например:
int iV, jV; int* pV=&iV; // pV - это адрес, //а *pV - значение int** ppV=&pV; int*** pppV=&ppV; iV=77; // Эквивалентно **ppV=77; jV=*pV; // Эквивалентно jV=**ppV; // или jV=***pppV;
Введение в программирование
Доступ к элементам структуры
Элементы структуры могут иметь модификаторы доступа: public, private и protected. По умолчанию все элементы структуры объявляются как общедоступные (public). Забегая вперед, следует сказать, что все члены класса по умолчанию объявляются как защищенные (private).Для обращения к отдельным элементам структуры используются операторы: . и ->.
Доступ к элементам структуры может иметь следующее формальное описание:
переменная_структурного_типа.элемент_структуры=значение; имя_структурного_типа *указатель_структуры= & переменная_структурного_типа; указатель_структуры->элемент_структуры=значение;
Например:
struct structA { char c1; char s1[4]; float f1;} aS1, // aS1 - переменная структурного типа *prtaS1=&aS1; // prtaS1 - указатель на структуру aS1 struct structB { struct structA aS2; // Вложенная структура } bS1,*prtbS1=&bS1; aS1.c1= 'Е'; // Доступ к элементу c1 структуры aS1 prtaS1->c1= 'Е'; // Доступ к элементу c1 через // указатель prtaS1 (*prtaS1).c1= 'Е'; // Доступ к элементу c1 (prtbS1->aS2).c1='Е'; // Доступ к элементу вложенной структуры
Доступ к элементу массива структурного типа имеет следующий формальный синтаксис:
имя_массива[индекс_элемента_массива].элемент_структуры
При использовании указателей на массив структур следует сначала присвоить указателю адрес первого элемента массива, а затем реализовывать доступ к элементам массива, изменяя этот указатель адреса.
Например:
struct structA { int i; char c;} sA[4], *psA; psA=&sA[0]; … cout<
Объединения
Объединение позволяет размещать в одном месте памяти данные, доступ к которым реализуется через переменные разных типов.Использование объединений значительно экономит память, выделяемую под объекты.
При создании переменной типа объединение память под все элементы объединения выделяется исходя из размера наибольшего его элемента. В каждый отдельный момент времени объединение используется для доступа только к одному элементу данных, входящих в объединение.
Так, компилятор Visual C++ выделит 4 байта под следующее объединение:
union unionA { char ch1; float f1;} a1;
| char ch1 | |||
| 1 | |||
| float f1 | |||
| 1 | 2 | 3 | 4 |
Объединения, как и структуры, могут содержать битовые поля.
Инициализировать объединение при его объявлении можно только заданием значения первого элемента объединения.
Например:
union unionA { char ch1; float f1;} a1={ 'M' };
Доступ к элементам объединения, аналогично доступу к элементам структур, выполняется с помощью операторов . и ->.
Например:
union TypeNum { int i; long l; float f; }; union TypeNum vNum = { 1 }; // Инициализация первого элемента объединения i = 1 cout<< vNum.i; vNum.f = 4.13; cout<< vNum.f;
Элементы объединения не могут иметь модификаторов доступа и всегда реализуются как общедоступные (public).
Объявление структуры
Структуры языка C++ представляют поименованную совокупность компонентов, называемых полями, или элементами структуры. Элементом структуры может быть:Объявление структуры имеет следующее формальное описание:
struct [имя_структуры] { тип_элемента_структуры имя_ элемента1; тип_элемента_структуры имя_ элемента2; ... тип_элемента_структуры имя_ элементаN; } [список_объявляемых_переменных];
Объявление структуры с битовыми полями имеет следующее формальное описание:
struct [имя_структуры] { тип_элемента_структуры [имя_ элемента1] : число_бит; тип_элемента_структуры [имя_ элемента2] : число_бит; ... тип_элемента_структуры [имя_ элементаN] : число_бит; } [список_объявляемых_переменных];
Возможно неполное объявление структуры, имеющее следующее формальное описание:
struct имя_структуры;
При отсутствии имени объявляемой структуры создается анонимная структура. При создании анонимной структуры обычно указывается список объявляемых переменных.
Список объявляемых переменных типа данной структуры может содержать:
Например: struct sA {char a[2], int i;} struA, struB[10], *struC;
Для использования указателя на структуру ему необходимо присвоить адрес переменной типа структуры.
Размер структуры с битовыми полями всегда кратен байту. Битовые поля можно определять для целочисленных переменных типа int, unsigned int, char и unsigned char. Одна структура одновременно может содержать и переменные, и битовые поля. Если для битового поля не задано имя элемента, то доступ к такому полю не разрешен, но количество указанных бит в структуре размещается.
Типом элемента структуры может быть:
Например:
struct sA {char a[2], sA* this_struct;}; // Корректное объявление структуры struct sB; // Неполное объявление структуры struct sA {char a[2], sB* this_struct;}; // Корректное объявление структуры struct sA {char a[2], sA this_struct;}; // Ошибочное объявление
Структура не может содержать в качестве вложенной структуры саму себя, но она может содержать элемент, являющийся указателем на объявляемую структуру.
Например:
struct structA { struct structA *pA; int iA; } sA; // pA указатель на структуру
При одновременном объявлении структурного типа, объявлении переменной данного типа и ее инициализации список значений указывается в фигурных скобках в последовательности, соответствующей последовательности определения элементов структуры.
Например:
struct POINT // Объявление структурного // типа POINT { int x; // Объявление элементов x и y int y; } p_screen = { 50, 100 }; // Эквивалентно записи p_screen.x = 50; // и p_screen.y = 100;
Перечисления
Перечисление, или перечислимый тип определяет множество, состоящее из значений, указанных через запятую в фигурных скобках.Перечисление задает для каждого мнемонического названия в указываемом множестве свой индекс.
Перечисление может иметь следующее формальное описание:
enum имя_типа {список_значений} список_объявляемых_переменных; enum имя_типа список_объявляемых_переменных; enum (список_элемент=значение);
Перечислимый тип описывает множество, состоящее из элементов-констант, иногда называемых нумераторами или именованными константами.
Значение каждого нумератора определяется как значение типа int. По умолчанию первый нумератор определяется значением 0, второй - значением 1 и т.д. Для инициализации значений нумератора не с 0, а с другого целочисленного значения, следует присвоить это значение первому элементу списка значений перечислимого типа.
Например:
// Создание перечисления enum eDay{sn, mn, ts, wd, th, fr, st} day1; // переменная day1 будет принимать // значения в диапазоне от 0 до 6 day1=st; // day1 - переменная перечислимого типа int i1=sn; // i1 будет равно 0 day1= eDay(0); // eDay(0) равно значению sn enum(color1=255); // Объявление перечисления, определяющего // именованную целую константу color1 int icolor=color1; enum eDay2{sn=1, mn, ts, wd, th, fr, st} day2; // переменная day2 будет принимать // значения в диапазоне от 1 до 7
Для перечислимого типа существует понятие диапазона значений, определяемого как диапазон целочисленных значений, которые может принимать переменная данного перечислимого типа.
Для перечислимого типа можно создавать указатели.

Передача структур в качестве параметров
Переменные структурного типа и элементы структуры можно передавать в функции в качестве параметров.Передача параметров может выполняться:
При передаче параметра по указателю передается только указатель на структуру, при передаче по значению в стек копируется все содержание структуры.
Например:
struct structA { int i; char c;} sA, *psA=&sA; void F1(struct structA sA); // Передача параметров по значению void F2(struct structA *psA); // Передача параметров по указателю void F3(struct structA &sA); // Передача параметров по ссылке … void F2(struct structA *psA) { psA->i =10; } // Доступ к элементу структуры
При большой вложенности вызовов и использовании большого числа структур или их значительных размерах вызов по значению может привести к переполнению стека.
Функция может возвращать значение структурного типа или типа указателя на структуру.
Например:
struct structA { int i; char с;}; struct structA Function3(void); // Функция возвращает значение // структурного типа struct structA *Function4(void); // Функция возвращает указатель // на структуру
Выделение памяти
При создании переменной типа структуры:Рассмотрим пример выделения памяти под структуру
struct structA { char cA; char sA[2]; float fA;};
При создании переменной структурного типа:
structA s1;
будет выделено 7 байтов. Элементы структуры будут размещены в памяти в следующем порядке:
| char cA | char sA[2] | float fA | ||||
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
Рассмотрим пример выделения памяти под структуру
struct structB { int i1:2; int i2:3; int :6; unsigned int i3:4;};
При создании переменной структурного типа:
structB s2;
будет выделено 2 байта. Элементы структуры будут размещены в памяти в следующем порядке:

Рис. 8.1.
Для целочисленных значений, предусматривающих наличие знакового разряда (например, int), старший левый бит из общего числа битов, выделяемых под данное битовое поле, интерпретируется как знак. Например, битовое значение 11 для поля i1 будет восприниматься как -1, а значение 11 для поля i3 - как 3.
Введение в программирование
Файлы
Файл представляет собой упорядоченный набор элементов одного типа.Стандартные процедуры ввода и вывода используют предопределенный тип TextFile, или Text, который реализует файл как упорядоченный набор строк символов.
Объявление типа файл и создание переменных файлового типа имеет следующее формальное описание:
type тип_файла = file of тип; var идентификатор: file of тип_файла;
Тип, указываемый после ключевых слов file of, может быть производным типом или базовым типом. Так, в качестве типа можно указать string[число_символов] или array[нач_инд..кон_инд] of Char.
Объявление нетипизированного файла имеет следующее формальное описание:
var идентификатор: file;
Для создания переменной файлового типа следует предварительно объявить файловый тип, а затем объявить переменную созданного типа, или сразу объявить переменную, указав используемый тип как файловый.
Например:
type Phone = record Name: string[30]; PhoneNumber: string[20]; end; {Объявление файлового типа } PhoneList = file of Phone; var {Объявление переменной файлового типа} book1: PhoneList; {Объявление переменной с указанием типа как файлового типа} book2: file of Phone;

Массивы
В языке Object Pascal можно создавать одномерные и многомерные массивы. Все элементы массива имеют одинаковый тип.Одномерный массив языка Object Pascal имеет следующее формальное описание:
Идентификатор: array [нач_индекс..кон_индекс] of тип_массива;
Например:
type RealArr = array[1..100] of Real; var int1: array[1..20] of Integer; int2: array[1..20] of Integer; int3, int4: array[1..10] of Integer; real1: array[1..10] of Real; begin Int1 := Int2; end.
Массивы могут объявляться как константы.
Например:
type TCub = array[0..1, 0..1, 0..1] of Integer; const cub1: TCube = (((0, 1), (2, 3)), ((4, 5), (6,7))); { Элементы массива cub1 будут содержать следующие значения: cub1 [0,0,0] = 0 cub1 [0,0,1] = 1 cub1 [0,1,0] = 2 и т.д. }
Массив символов объявляется как массив переменных типа Char. Символьный массив может быть доступен через указатель на массив типа PChar.
Строки можно реализовывать как символьные массивы, ограниченные 0-символом (#0).
Например:
const TempString: array[0..8] of Char = 'Строка'#0; var P: PChar; {Указатель на строку} begin P := 'Строка'; P := @TempString; {Переменная P указывает на значение 'Строка'} end.
Массив символов типа Char и значение типа PChar эквивалентны.
Например:
var MyArray: array[0..5] of Char; MyPointer: PChar; begin MyArray := 'array'; MyPointer := MyArray; MyProc(MyArray); {Эквивалентно вызову MyProc(MyPointer);} end.
Многомерный массив имеет следующее формальное описание:
array[диапазон_первого_индекса, ..., диапазон_n_индекса] of базовый_тип; array[диапазон] of array[диапазон] of array[диапазон] of тип;
Так, объявление
type MyMassiv = array[1..10] of array[1..70] of Integer;
эквивалентно объявлению
type MyMassiv = array[1..10, 1..70] of Integer;
Например:
type MyAr = array[1..10, 1..50] of Real; begin MyAr[1,49]:= 49; end.
По принципу выделения памяти под массив массивы языка Object Pascal делятся на статические и динамические.
Память под статический массив выделяется при его создании.
Динамический массив не имеет фиксированного размера или длины.
При объявлении динамического массива память под него не выделяется. Выделение памяти происходит каждый раз при присвоении значения массиву или при вызове процедуры SetLength.
Создание динамического массива имеет следующее формальное описание:
array of тип_элементов_массива;
Например:
{Объявление динамического массива} var MyAr: array of Integer; {Определение количества элементов в динамическом массиве:} SetLength(MyAr, 10);
Элементы динамического массива всегда индексируются с 0. Переменная типа динамического массива является указателем, и к ней применимы операции над указателями. Для освобождения памяти, занимаемой динамическим массивом, можно присвоить значение nil переменной, ссылающейся на массив. Количество элементов динамического массива определяется процедурой SetLength. Функция Length возвращает количество элементов в динамическом массиве.
При сравнении двух переменных типа динамического массива выполняется сравнение их ссылок, а не значений их элементов. Например:
var A, B: array of Integer; begin SetLength(A, 1); SetLength(B, 1); A[0] := 2; B[0] := 2; {A[0] = B[0] вернет значение True} {A = B вернет значение False} end.
Для усечения динамического массива можно использовать функцию Copy.
Например:
var A: array of Integer; begin SetLength(A, 50); {Оставляет 10 элементов массива A} A := Copy(A, 0, 10); end.
Если при объявлении функции или процедуры формальный параметр определяется как array of тип_массива, то эта функция или процедура может получить в качестве фактического параметра любой динамический или статический массив указанного типа вне зависимости от его размера и индексации его элементов.
Множества
язык Object Pascal предоставляет два простых типа, описывающих множества значений. Это:Множество языка Object Pascal - это набор значений порядкового или перечислимого типа. Множество определяется ключевым словом set of.
Создание порядкового и перечислимого типов имеет следующее формальное описание:
Идентификатор = Начальное_значение .. Конечное_значение; Идентификатор = {Значение_i .,...}; Идентификатор = Значение_i..Значение_j; Идентификатор = set of порядковый или перечислимый тип.
Например:
type {Объявление перечислимого типа} TMyColor = {Red,Blue,Yellow}; {Объявление типа множество} TMyClr = set of TMyColor; var {Объявление переменных созданных типов} ValC1, ValC2: TMyClr; begin {Присвоение значения переменной типа множество} ValC1 := [Blue]; ValC2 := ValC1+[ Red]; end.
Начальным и конечным значением интервала для порядкового типа могут быть:
Перед работой с переменной множественного типа ее следует проинициализировать. Значения инициализации указываются в квадратных скобках.
Например:
type TMyInts = 1..500; TIntSet = set of TMyInts; {Объявление переменной типа множества} var set1: TIntSet; begin {Инициализация переменной типа множества} set1:=[1,2,3,4]; end.
Над множеством допустимы следующие операции:
Например:
type {Порядковый тип: значения от 1 до 9} CountType = 1..9; LowerCharType = а..я; AType = (A1, A2, A3, A4, A5); {Порядковый тип: значения A3, A4 и A5} AType3 = A3..A5; {Множество на основе порядкового типа} ATypeSet = set of AType3; {Множество LetterType } LetterType = set of 'A'..'Z'; var {Объявление переменной типа множества} aset: ATypeSet; begin {Присвоение значения переменной множественного типа} aset:= [A3] + [A4]; {Вычитание множеств} aset:= aset - [A4]; end.
Каждая переменная множественного типа может иметь значение - множество. Для изменения значений переменной множественного типа могут использоваться операции объединения (+) и разности (-) множеств.
Например:
begin set1:=set1 +[5,6]; end.
Для определения принадлежности значения некоторому множеству используется операция in.
Например:
begin if 5 in set1 then ShowMessage( 'Значение принадлежит множеству set1'); end.
Объявление производного типа
Кроме базовых типов данных, рассмотренных в лекции 6, язык Object Pascal предоставляет простые и структурированные типы данных.Используя базовые, простые и структурированные типы данных, можно создавать производные типы данных.
Напомним, что к базовым типам относятся:
К простым типам относятся:
К структурированным типам относятся:
Для того чтобы создать переменную производного типа, сначала следует определить этот производный тип. Объявление нового производного типа указывается ключевым словом type.
Объявление произвольного типа как нового имени существующего типа в языке Object Pascal имеет следующее формальное описание:
type имя_нового_типа = имя_существующего_типа;
Объявление произвольного типа как нового типа в языке Object Pascal имеет следующее формальное описание:
type имя_нового_типа = type имя_существующего_типа;
При объявлении произвольного типа как нового имени типа, тип переменных производного типа и типа, используемого для его создания, будет совпадать. Если перед именем существующего типа указать ключевое слово type, то компилятор создаст новый тип. При этом указание в качестве формального параметра подпрограммы переменной существующего типа, а в качестве фактического параметра подпрограммы - переменной нового типа, созданного на базе данного существующего типа, вызовет для var-параметров ошибку времени компиляции.
В языке Object Pascal объявление типов и переменных может быть выполнено только в определенных местах программы: секция объявления типа type используется для объявлений типов, а секция var для объявления переменных.
Например:
type TValReal1 = Real; var // Переменные X и Y имеют // один и тот же тип X: Real; Y: TValReal1; Е type TValReal2 = type Real; var // Переменные X и Y имеют разные типы X: Real; Y: TValReal2;
Записи
Запись представляет собой структуру, состоящую из набора полей различных типов. Каждый элемент записи называется полем. Каждое поле имеет свой идентификатор и свой тип.Запись может иметь поля двух видов:
Объявление типа записи завершается ключевым словом end;.
Например:
type MyDateRec = record Year: Integer; Month: 1..12; Day: 1..31; end; var {Объявление переменных} Record1, Record2: MyDateRec; begin {Доступ к полям записи: } Record1.Month := 11; Record1.Day := 22; {Доступ к полям записи:} with Record1 do begin Year := 2004; Month := 11; Day := 22; end; {Копирование значений полей записей одного типа } Record2:= Record1;
Переменная типа записи может быть объявлена одновременно с определением полей записи.
Например:
var S: record Name: string; Age: Integer; end;
Запись, состоящая из одних вариантных полей, реализует тип, в некотором роде аналогичный типу union для языка С++.
Вариантные поля позволяют на основе одного типа записи создавать экземпляры записей, содержащие поля разных типов. Это позволяет значительно экономить память.
Объявление записи имеет следующее формальное описание:
type имя_типа_записи = record список_полей: тип; {фиксированные поля} ... case тег: тип_варианта of {вариантные поля} константа_или_список: (вариант_1); ... константа_или_список: (вариант_n); end;
Описание каждого фиксированного поля состоит из его идентификатора (или списка идентификаторов) и типа. Описания полей разделяются точкой с запятой. Вариантная часть объявления записи начинается с зарезервированного слова case.
При определении вариантной части тег и символ двоеточия могут быть опущены в том случае, если тип_варианта описывает существующий тип или каждая константа (константа_или_список) в вариантной части является значением типа тип_варианта.
Тип варианта не может быть длинной строкой, динамическим массивом, типом Variant или интерфейсом, но он может быть указателем на эти типы.
Например:
type TPerson = record FirstName, LastName: string[40]; case bl: Boolean of {Вариантные поля} True: (str1: string[40]); False: (str2: string[20]; date1: TDate); end; TShapeList = (Rectangle, Circle, Other, Ellipse); TFigure = record case TShapeList of Rectangle: (Height, Width: Real); Circle: (Radius: Real); Ellipse, Other: (); end;
Применяя вариантные поля, можно одинаково трактовать данные различных типов, используя для доступа к ним вариантные поля соответствующих типов.
Введение в программирование
Конструкторы класса
Конструктором называется метод, вызываемый при создании объекта данного класса. Класс может иметь несколько конструкторов, отличающихся списком параметров.Деструктором называется метод, вызываемый при разрушении объекта данного класса. Имена конструктора и деструктора совпадают с именем класса, но перед именем деструктора указывается символ ~.
При создании объектов последовательно вызываются конструкторы всех его базовых классов. Вызов деструкторов при уничтожении объекта происходит в обратном порядке.
Если конструктор базового класса имеет список параметров, то для его использования в производном классе следует создать конструктор производного класса с таким же списком параметров.
При создании нового класса среда Visual C++ автоматически создает заголовочный файл и файл реализации класса с конструктором и деструктором без параметров.
Квалификация имен
Квалификация имен используется для однозначного понимания указываемого имени.Для квалификации имени могут использоваться следующие операторы: :: (оператор принадлежности); . (оператор доступа к члену класса посредством имени); -> (оператор доступа к члену класса через указатель).

Объектно-ориентированное программирование на языке С++
В настоящее время понятие языка программирования неотрывно связано со средой программирования, в которой разрабатываются приложения. Для языка С++ наиболее развитыми и популярными средами программирования являются:Среда программирования Visual Studio.NET предназначена для создания приложений не только на языке С++, но и на таких популярных языках, как C# и Visul Basic. Иногда для сокращения говорят, что проектирование приложений на C++ в Visual Studio.NET реализуется средой Visual C++.
Visual C++ позволяет разрабатывать приложения как в терминах традиционного модульного программирования, так и с применением объектно-ориентированного программирования.
Объектные типы
Тип данных всегда определяет размер памяти, которая будет выделена под переменную данного типа при ее создании.При объявлении переменной объектного типа (типа класса) создаются переменные члены класса и вызывается конструктор класса. Производные типы на основе классов позволяют получать доступ к членам класса.
Переменная, объявленная как указатель на класс, применяется для доступа к методам и переменным членам класса. В следующей таблице приведен синтаксис, используемый при работе с указателями на члены класса.
Hапример:
#include
Объявление и реализация класса в языке С++
Создаваемый класс должен быть объявлен и реализован.Объявление класса в языке С++ может иметь следующее формальное описание:
class имя_класса : список_базовых_классов { public: // Модификатор доступа относится // ко всем перечисленным после // него членам до следующего // модификатора доступа // Объявление общедоступных членов класса
protected: // Объявление членов класса, доступных // только для производных классов
private: // Объявление защищенных членов класса };
Список базовых классов указывается после имени класса через символ двоеточия (:), разделяется запятыми и может иметь модификаторы доступа.
Например:
class MyClass : public ClassA, public ClassB, private ClassC {};
В языке С++ считается, что если модификатор доступа для класса или члена класса не указан, то по умолчанию предполагается модификатор доступа private (защищенный доступ). Для членов структур, объявляемых ключевым словом struct, по умолчанию модификатор доступа предполагается равным public.
Модификатор доступа базового класса позволяет определить, какие переменные и методы базового класса будут доступны из производного класса. Модификатор доступа, указываемый перед именем базового класса, определяет следующие правила доступа к переменным и методам базового класса из производного класса:
Например:
class BaseClass { public: int PublicFunc(); protected: int ProtectedFunc(); private: int PrivateFunc(); }; class DerivedClass1 : public BaseClass { // Наследуемая функция PublicFunc доступна // как public // Наследуемая функция ProtectedFunc // доступна как protected }; class DerivedClass2 : private BaseClass { // Наследуемая функция PublicFunc доступна // как private // Наследуемая функция ProtectedFunc // доступна как private }; int main() { }
В теле объявления класса указываются модификаторы доступа, описывающие права доступа для переменных и методов класса:
Для доступа к членам класса используется операция принадлежности ::, указываемая после идентификатора класса. Для доступа к членам экземпляра класса используются операции . и ->.
Для доступа к объекту самого класса внутри метода члена класса используется ключевое слово this.
Например:
class A { public: int i; Func1();} A:: Func1() { return this->i; } // this - указатель класса A
Преобразование объектных типов
Указатель на класс может быть преобразован к указателю на базовый класс в двух случаях:Например:
class A // А - базовый класс {public: int a1; int Fa(); }; class B : public A // В - производный класс {public: int b1; int Fb(); }; B bObj; // Объект типа B A *pA = &bObj; // Преобразование типа ограничивает // доступ к членам класса B и // разрешает доступ только к членам // базового класса B *pB = &bObj; // Нет преобразования типа и // следовательно разрешен доступ как // к членам класса B, // так и к членам класса А. pA-> Fa (); // Правильно: вызов функции члена класса A. pB-> Fa (); // Правильно: вызов функции, //наследуемой от класса A // Вызов pA-> Fb (); ошибочен: функция Fb // недоступна через указатель на базовый // класс А
Указатели на члены класса или структуры не могут рассматриваться как обычные указатели и для них не выполняется стандартное преобразование типа.
Создание объекта
Для создания объекта (экземпляра данного класса) следует объявить переменную типа указатель на класс, а затем создать объект, выполнив оператор new с указанием используемого конструктора.Например:
A* ca; ca= new A();
Эти же действия можно записать одним оператором.
Например:
A* ca= new A();
Для того чтобы можно было использовать конструктор с параметрами, значения параметров необходимо указать при создании объекта.
Структура приложения на языке С++
Приложение состоит из модулей трансляции - файлов исходного кода на языке С++. Каждый модуль трансляции представляется файлом реализации класса и заголовочным файлом (компилятор позволяет записывать всю информацию - объявление и реализацию, в один файл, но это оправданно только для маленьких программ).Проектом в терминологии Visual C++ называется совокупность всех модулей трансляции, файлов ресурсов и описания конфигурации.
Разработка любого приложения в Visual C++ начинается с создания проекта. Visual Studio.NET предоставляет шаблоны для создания различных видов приложений (консольные приложения, MFC-приложения, DLL-библиотеки, приложения управляемого кода и т.п.).
Консольное приложение - это приложение, не использующее Windows-окна для обработки сообщений от пользователя. Точкой входа в консольное приложение в языке С++ является метод main.
После того как создан шаблон приложения заданного вида, информация обо всех файлах проекта отображается в окне проектов среды Visual C++.
Заголовочный файл содержит объявления используемых данных. Язык C++ поддерживает соглашение о раздельной компиляции: каждый С++-модуль можно компилировать отдельно. Для того чтобы несколько модулей могли использовать одни и те же данные, объявление этих данных выносят в заголовочный файл. Принято, что имя заголовочного файла имеет расширение h.
Все подключаемые к модулю заголовочные файлы указываются в начале модуля директивой препроцессора #include.
Например:
#include "stdafx.h" #include
Имя подключаемого файла в директиве препроцессора #include может быть указано:
Файл, содержащий реализацию методов, объявленных в заголовочном файле, иногда называется исходным файлом.
Каждая программа должна иметь точку входа и может содержать описание одного или нескольких классов.
Точкой входа в программу в приложении, формируемом по шаблону в среде Visual C++, является метод _tmain, заменяемый при компиляции на метод main.
Минимальной программой на С++ является следующий код:
int main() {}
Эта программа определяет функцию с именем main, которая не использует параметров и не выполняет никаких действий.
Тело метода в языке С++ указывается в фигурных скобках. Перед именем метода указывается тип возвращаемого значения. Если метод не возвращает никакого значения, то его тип обозначается ключевым словом void.
Терминология объектно-ориентированного программирования
Объектно-ориентированное программирование позволяет оперировать в терминах классов: определять классы, конструировать производные классы, создавать объекты, принадлежащие классу, - экземпляры класса.Сначала в некоторых языках программирования появился тип struct, расширением которого стал тип class.
Класс определяет данные (переменные) и поведение (методы). Данные и методы класса также называют членами класса. Класс рассматривается как определяемый пользователем тип данных.
Объектом называется экземпляр некоторого класса. Объект создается как переменная типа класса, которая используется для доступа к данным - членам класса и для вызова методов - членов класса.
Наследованием называется механизм, позволяющий производному классу наследовать структуру данных и поведение другого класса, а также наследовать поведение, объявленное в интерфейсах и абстрактных классах.
Наследование позволяет определять новые классы в терминах существующих классов.
В объектно-ориентированном программировании наследование может быть:
Наследуемый класс принято называть базовым классом, или родительским классом (классом - предком, суперклассом).
Производный класс, наследующий структуру данных и поведение своего базового класса, иногда также называется дочерним классом (классом - потомком, подклассом).
В производном классе можно переопределять методы базового класса и добавлять новые методы. Непосредственным базовым классом называется класс, от которого порожден производный класс следующего уровня иерархии:
| А | Базовый класс класса С и непосредственный базовый класс класса B |
| ? | |
| B | Непосредственный базовый класс класса C |
| ? | |
| C | Производный класс |
Полиморфизмом называется способность различных объектов по-разному обрабатывать одинаковые сообщения.
Инкапсуляция позволяет работать в терминах объектов и скрывать их переменные и методы. Использование инкапсуляции дает возможность модифицировать внутреннюю реализацию объекта без влияния на программу в целом до тех пор, пока не изменяется интерфейс с объектом.
В языках программирования инкапсуляция поддерживается реализацией модификаторов доступа, таких как protected - для защищенных членов класса на уровне класса, и private - для полностью защищенных членов класса.
Вложенные классы
Язык С++ допускает использование вложенных классов - внутри тела одного класса содержится объявление других классов.Например:
class A { public: A(void); ~A(void); class B { // Вложенный класс B(void) {}; ~B(void) {}; char sStr2[3]; };
};
Введение в программирование
Дружественные члены класса
Дружественные члены класса (методы) позволяют получить доступ к защищенным модификатором private членам класса из методов других классов. Методы и классы, объявляемые дружественными, иногда также называются друзьями класса.Если метод класса A внутри тела класса B объявляется с модификатором friend, что указывает на то, что он является другом класса, то из него разрешен доступ ко всем членам класса B.
Например:
class A { public: int Fx();} class B { public: friend int A::Fx(); private: }
Дружественные классы
Объявление дружественного класса позволяет всем его методам получить доступ ко всем переменным и методам другого класса.Например:
class A {public: int Fx();} class B {public: friend class A; private: }
Дружественный класс или член класса будет доступен только в том случае, если он был объявлен в области видимости самого класса или ранее во внешней области видимости, внутри которой располагается область видимости, содержащая объявление класса с объявлениями друзей класса.
Например:
class A {public: // Класс расположен во внешней // области видимости int Fx1(); } namespace classB { class B {public: friend class A; friend class C; private: } class C { public: // Класс расположен в том же // пространстве имен int Fx2(); } }
Дружественные классы не наследуются, и их дружественность не является транзитивной.
Например:
class A {int Fx();} class B {friend class A;} class C {friend class B;} // Класс А не является // дружественным классу C class D : public B {} // Класс А не является // дружественным классу D

Методы-члены класса
В терминологии объектно-ориентированного программирования функции также называются методами или методами - членами класса, для того чтобы подчеркнуть, что конкретная функция является членом некоторого класса.По стандарту ANSI C, используемому компилятором Visual C++, любая используемая в модуле компиляции функция должна иметь прототип, включающий в себя тип функции, имя функции и список параметров с их типами. Прототип - это некоторое предварительное описание функции, заканчивающееся символом ;.
Прототип функции должен быть вставлен во все модули компиляции, использующие функцию. Для небольших программ прототип функции обычно записывается в модуль компиляции. Для программ, создаваемых средствами Visual C++, и программ, состоящих из нескольких модулей, прототип функции указывается в заголовочном файле. Объявление функции должно однозначно соответствовать ее прототипу и может находиться в любом модуле компиляции (для которого доступен прототип функции).
Прототип метода, являющегося членом некоторого класса, записывается в заголовочном файле, содержащем объявление класса в теле этого класса.
Метод - член класса может иметь следующие модификаторы доступа:
Прототип метода может иметь следующее формальное описание:
модификатор_доступа тип_метода имя_метода (список_параметров);
Объявление метода может иметь следующее формальное описание:
модификатор_доступа тип_метода имя_метода (список_параметров) { тело_метода }
Тип метода является типом возвращаемого методом значения и может быть любым допустимым базовым или производным типом, включая и тип указателя.
Указатели типа void могут использоваться для объявления параметров метода в том случае, если тип этих параметров на момент компиляции неизвестен.
Например:
void Fx(void *pV); //Прототип метода ...
piVar=new int; pfVar=new float; Fx(piVar); // Вызов метода для // параметра типа int Fx(pfVar); // Вызов метода для // параметра типа float ... // Реализация метода void Fx(void *pV) {*pV=12345;}
Метод, возвращающий указатель, записывается следующим образом:
тип *имя_метода(список_параметров).
Список формальных параметров - это значения, передаваемые в функцию. Каждый элемент этого списка может иметь любой допустимый тип.
Язык C++ допускает передачу параметров по значению и по ссылке или указателю. Параметры, передаваемые по значению, при выполнении программы заносятся в стек, и для больших размеров структур или массивов может возникнуть ситуация переполнения стека. Параметры, передаваемые как ссылки или указатели, не копируются в стек.
Изменение значений самих параметров происходит только для параметров, переданных по ссылке или указателю. При передаче параметра по значению метод изменяет не сам параметр, а его копию.
Список параметров может включать параметры, имеющие значения по умолчанию. Такие параметры указываются в конце списка параметров. Значение по умолчанию будет использовано только в том случае, если число переданных параметров меньше числа указанных формальных параметров в описании метода.
Язык C++ разрешает рекурсивный вызов методов.
В методах класса можно использовать ключевое слово this, являющееся указателем на объект данного класса. Это ключевое слово нельзя использовать вне метода члена класса.
Методы с переменным числом параметров
Метод может иметь переменное число параметров: это указывается в конце списка параметров символом многоточия ... . Методы с переменным числом параметров должны иметь как минимум один описанный формальный параметр.Для применения переменного числа параметров можно использовать:
Например:
#include
Перегрузка функций и методов
Язык C++ позволяет выполнять перегрузку функций и методов - членов класса, то есть вызывать функцию (метод) с тем же именем, но с различными типами фактических параметров. Для создания перегружаемой функции следует указать отдельный прототип и сделать отдельное объявление для каждого списка параметров. Если функции различаются только типом и имеют совпадающие списки параметров, то для таких функций перегрузка не допускается.Для определения вызываемой перегружаемой функции учитываются только списки параметров, а тип возвращаемого значения не учитывается. При этом для соответствия списка параметров вызываемой функции может применяться преобразование типов и соответствие по многоточию (соответствие по многоточию?) для функций с переменным числом параметров.
Создание метода-члена класса
Для того чтобы в среде Visual Studio .NET добавить в класс новый метод - член класса, следует в окне просмотра классов Class View выделить секцию с именем класса и выполнить команду контекстного меню Add|Add Function, а затем в диалоге - мастере построения метода ввести имя метода, его тип, а также определить список параметров.При добавлении метода - члена класса в файл реализации класса автоматически добавляется код реализации нового метода, а в заголовочный файл - объявление этого метода.
Указатели на методы-члены класса и на функции
Указатели на методы и на функции могут быть использованы для передачи метода в качестве параметра другому методу.Объявление указателя на метод может иметь следующее формальное описание:
тип_метода (имя_класса::*имя_метода_указателя) (список параметров); тип_функции (*имя_ функции_указателя) (список параметров);
Инициализация указателя может иметь следующее формальное описание:
тип_метода имя_класса::*имя_метода_указателя (список параметров)= &имя_класса::имя_любого_метода; тип_функции (*имя_ функции_указателя) (список параметров)= &имя_функции;
Вызов метода, объявленного как указатель на метод, может быть выполнен следующим образом:
(имя_объекта->*имя_ метода_указателя) (список параметров); (*имя_ функции_указателя)(список параметров);
Для функций, но не для методов - членов класса, разрешена другая форма вызова метода:
имя_ функции_указателя(список параметров); (имя_ функции_указателя)(список параметров);
Объявление функции, имеющей в качестве параметра указатель на метод, может иметь следующее формальное описание:
тип_метода имя_метода (тип_метода_указателя (*имя_метода_указателя) (список параметров));
Вызов метода, использующего в качестве параметра указатель на метод, может иметь следующее формальное описание:
имя_метода(имя_объекта->*имя_метода_указателя); имя_функции(имя_функции_указателя);
Разрешается создавать массив указателей на функции.
При использовании указателей на функцию можно не употреблять операцию разыменования или операцию получения адреса.
Например:
class a1 { public: a1(void); ~a1(void); int Fx1(int i1); int Fx2(int i2); }; a1::a1(void){} a1::~a1(void){} int a1::Fx1(int i1){ return 1;} int a1::Fx2(int i2){ return 2;}
int (*Fy_pointer)(); // Объявление указателя на функцию int Fy (); int _main(int argc, char* argv[]) { a1* a1Object = new a1(); int (a1::*Fx_pointer)(int)=&a1::Fx2; // Объявление и инициализация указателя // на метод - член класса int i; i=(a1Object->*Fx_pointer)(1); // Вызов по указателю на метод std::cout<
Встроенные функции
Встроенные функции указываются оператором inline. Применение встроенных функций может несколько сократить время выполнения программы, так как компилятор встраивает код такой функции в том месте программы, где указан ее вызов.Например:
inline void Fx(void) { std::cout<<"Функция Fx"<
Введение в программирование
Абстрактные классы
Абстрактным классом называется класс, который содержит хотя бы одну чисто виртуальную функцию.Абстрактный класс не может быть явно использован для создания объектов.
Как правило, абстрактный класс применяется для описания интерфейса, который должен быть реализован всеми его производными классами.
Если класс, производный от абстрактного класса, не содержит реализации всех его чисто виртуальных функций, то он также является абстрактным классом.
Указатель на объект класса
|
class A // Объявление базового класса { public: virtual void VirtMetod1(); // Виртуальный метод void Metod2(); // Не виртуальный метод }; void A::VirtMetod() { cout << "Вызван A::VirtMetod1\n";} void A::Metod2() { cout << "Вызван A::Metod2\n"; } class B : public A // Объявление производного класса {public: void VirtMetod1(); // Виртуальный метод void Metod2(); // Не виртуальный метод }; void B::VirtMetod1() { cout << "B::VirtMetod1\n";} void B::Metod2() { cout << "B::Metod2\n"; } void main() { B aB; // Объект класса B B *pB = &aB; // Указатель на объект класса B A *pA = &aB; // Указатель на объект класса A pA->VirtMetod1(); // Вызов метода VirtMetod класса B pB->VirtMetod1(); // Вызов метода VirtMetod класса B pA->Metod2(); // Вызов метода Metod2 класса A pB->Metod2(); // Вызов метода Metod2 класса B } |
| Пример 12.1. Вызов виртуальных методов |
| Закрыть окно |
class A // Объявление базового класса
{ public:
virtual void VirtMetod1(); // Виртуальный метод
void Metod2(); // Не виртуальный метод
};
void A::VirtMetod() { cout << "Вызван A::VirtMetod1\n";}
void A::Metod2() { cout << "Вызван A::Metod2\n"; }
class B : public A // Объявление производного класса
{public:
void VirtMetod1(); // Виртуальный метод
void Metod2(); // Не виртуальный метод
};
void B::VirtMetod1() { cout << "B::VirtMetod1\n";}
void B::Metod2() { cout << "B::Metod2\n"; }
void main() {
B aB; // Объект класса B
B *pB = &aB; // Указатель на объект класса B
A *pA = &aB; // Указатель на объект класса A
pA->VirtMetod1(); // Вызов метода VirtMetod класса B
pB->VirtMetod1(); // Вызов метода VirtMetod класса B
pA->Metod2(); // Вызов метода Metod2 класса A
pB->Metod2(); // Вызов метода Metod2 класса B
}
Реализация методов шаблона класса template
|
template int main(int argc, char* argv[]) { int i1=1; int i2=2; double x=1.5; double y=2.5; A |
| Пример 12.2. Создание объекта на основе шаблона класса |
| Закрыть окно |
template // Описание шаблона класса
class A
{
T1 x,y;
public:
A();
~A();
T1 Fx();
void Fy(T1 a, T1 b);
};
template A< T1>::A() {} // Реализация методов шаблона класса
template A< T1>::~A() {}
template T1 A< T1>::Fx(){ return x*y;}
template void A< T1>::Fy( T1 a, T1 b) {x=a; y=b;}
int main(int argc, char* argv[])
{ int i1=1;
int i2=2;
double x=1.5;
double y=2.5;
A F1; // Создание объекта с использованием шаблона класса
A *pF1=&F1; // Указатель на объект параметризированного типа
pF1->Fy(i1,i2);
std::cout
A *pF2=&F2;
pF2->Fy(x,y);
std::cout
Работа с памятью
Свободная память, которую можно заказывать в процессе выполнения программы, называется динамической памятью. Для выделения непрерывного блока памяти можно использовать функцию malloc(), а для освобождения - функцию free(). Прототипы этих функций расположены в заголовочном файле stdlib.h.Например:
int *pIntBuffer; // Выделение памяти под 5 переменных типа int pIntBuffer=malloc(5*sizeof(int));
При отсутствии требуемого количества памяти функция malloc возвращает значение null.
Выделение динамической памяти также можно выполнять оператором new, а освобождение - оператором delete. Оператор new автоматически учитывает тип объекта и выделяет требуемое количество памяти.
Пример:
double *pBuffer; // Выделение памяти под 10 переменных типа double pBuffer=new double [10];

Шаблоны классов
Шаблоны классов позволяют определить параметризированный тип.Шаблоны классов аналогично шаблонам функций позволяют на этапе компиляции создавать определения классов.
Шаблон класса указывается ключевым словом template, за которым в угловых скобках указывается список типов для создания параметризированного класса. Имя шаблона класса указывается после ключевого слова class.
Для создания объекта на основе шаблона класса после имени шаблона в угловых скобках указывается конкретный тип:
имя_шаблона_класса <тип> имя_объекта.
Например:
template
int main(int argc, char* argv[]) { int i1=1; int i2=2; double x=1.5; double y=2.5; A
Шаблоны методов
Шаблоны методов позволяют описывать одинаковую реализацию для различных типов параметров.Шаблон метода позволяет описать одну реализацию метода для всех допустимых значений параметров. Фактически шаблон метода заменяет набор перегружаемых методов с различными типами параметров.
Шаблон метода - это еще одно проявление полиморфизма языка С++.
Шаблоны методов используют в качестве параметра тип переменной.
В момент компиляции при вызове метода, не имеющего прототипа, выполняется поиск шаблона метода, из которого вызываемый метод может быть сгенерирован.
Шаблон метода указывается ключевым словом template, за которым в угловых скобках указывается список типов, используемых для параметров функции и типа возвращаемого значения.
Например:
template
При использовании шаблона компилятор заменит для вызываемого метода тип шаблона на тип, указанный при вызове метода (T1 на int в следующем примере).
Например:
int i1=1; int i2=2; std::cout<
Статические члены класса
Переменные и члены класса, объявленные с модификатором доступа static, называются статическими членами класса. Cтатические переменные и методы доступны без создания объекта данного класса. Имя статической переменной квалифицируется именем класса с использованием операции принадлежности :: , а не именем экземпляра класса.Например:
class A { public: static int iStat; }; int main(int argc, char* argv[]) { A:: iStat = 123; }
Статические методы могут вызывать и использовать только другие статические методы и статические переменные. Из статического метода нельзя выполнять вызов не статического метода - члена класса.
Статическую переменную можно рассматривать как аналог глобальной переменной, которая связана с конкретным классом.
Часто статические переменные называют переменными класса, а не статические переменные - переменными экземпляра.
Для статических переменных могут использоваться указатели.
Например:
class A { public: static int iStatVar; }; int main(int argc, char* argv[]){ int A:: iStatVar = 0; int *piStatVar = &A:: iStatVar; }
При использовании статических переменных указатели всегда являются указателями на существующий тип (в данном примере на тип int), а не на тип класса.
Для того чтобы создать и использовать статическую переменную, следует:
Например: static int iStat;.
Например: int A::iStat;
Например: A::iStat=123;
Инициализацию статической переменной можно выполнять в теле конструктора в том случае, если при создании объекта с использованием этого конструктора память под статическую переменную уже выделена. Однако инициализировать статическую переменную через конструктор в форме
имя_класса::имя_конструктора : имя_стат_переменной(значение) { }
нельзя ни в каком случае.
Для того чтобы создать статический метод - член класса в среде Visual C++, достаточно выделить в окне Class View секцию с именем класса, в который будет вставлен создаваемый метод, и выполнить команду контекстного меню Add|Add Function. А затем определить тип и имя создаваемого метода, список параметров, и отметить флажок Static, указывающий на то, что создаваемая функция будет статической.
В результате выполненных действий среда Visual C++ добавит в заголовочный файл класса прототип создаваемой статической функции, а в файл реализации - код реализации создаваемой статической функции.
Например:
class A {public: a1(void); ~a1(void); int Fx1(int i1); int Fx2(int i2); protected: static int Fstat1(void); // Статическая функция доступна //только для статических методов - членов //данного класса и наследуемых классов public: static int Fstat2(void); // Общедоступная статическая функция };
int main(int argc, char* argv[]){ std::cout<
Реализация статической функции записывается так же, как и реализация любого другого метода - члена класса. При этом ключевое слово static не указывается.
Например:
int A::FStat1(void) { return 0; }
Виртуальные классы
Для того чтобы при множественном наследовании один и тот же базовый класс не порождал для объекта производного класса несколько объектов базового класса, при объявлении производного класса такой базовый класс указывается с ключевым словом virtual и называется виртуальным классом.Например:
class A : virtual public B { }
Виртуальные методы
Виртуальные методы объявляются в базовом классе с ключевым словом virtual, а в производном классе могут быть переопределены. Прототипы виртуальных методов как в базовом, так и в производном классе должны быть одинаковы.Применение виртуальных методов позволяет реализовывать механизм позднего связывания, при котором определение вызываемого метода происходит на этапе выполнения, а не на этапе компиляции. При этом вызываемый виртуальный метод зависит от типа объекта, для которого он вызывается. При раннем связывании, используемом для не виртуальных методов, определение вызываемого метода происходит на этапе компиляции.
На этапе компиляции строится таблица виртуальных методов, а конкретный адрес проставляется уже на этапе выполнения.
При вызове метода с использованием указателя на класс действуют следующие правила:
В следующем примере иллюстрируется вызов виртуальных методов:
class A // Объявление базового класса { public: virtual void VirtMetod1(); // Виртуальный метод void Metod2(); // Не виртуальный метод }; void A::VirtMetod() { cout << "Вызван A::VirtMetod1\n";}
void A::Metod2() { cout << "Вызван A::Metod2\n"; }
class B : public A // Объявление производного класса {public: void VirtMetod1(); // Виртуальный метод void Metod2(); // Не виртуальный метод }; void B::VirtMetod1() { cout << "B::VirtMetod1\n";} void B::Metod2() { cout << "B::Metod2\n"; } void main() { B aB; // Объект класса B B *pB = &aB; // Указатель на объект класса B A *pA = &aB; // Указатель на объект класса A pA->VirtMetod1(); // Вызов метода VirtMetod класса B pB->VirtMetod1(); // Вызов метода VirtMetod класса B pA->Metod2(); // Вызов метода Metod2 класса A pB->Metod2(); // Вызов метода Metod2 класса B }
Пример 12.1. Вызов виртуальных методов
Результатом выполнения этой программы будут следующие строки:
Вызван B::VirtMetod1 Вызван B::VirtMetod1 Вызван A::Metod2 Вызван B::Metod2
Чисто виртуальной функцией называется виртуальная функция, указанная с инициализатором
=0.
Например:
virtual void F1( int) =0;
Объявление класса может содержать виртуальный деструктор, используемый для удаления объекта определенного типа. Однако виртуального конструктора в языке С++ не существует. Некоторой альтернативой, позволяющей создавать объекты заданного типа, могут служить виртуальные методы, в которых выполняется вызов конструктора для создания объекта данного класса.
Например:
class A{ public: A(); A (const A&); virtual A* virt_object1 () { return new A(); } virtual A* virt_object2 () { return new A(*this); } }
Введение в программирование
Абстрактные классы
Абстрактные классы содержат набор методов, который должен быть реализован в производном классе. Абстрактные классы и абстрактные методы помечаются модификатором abstract. Если в подклассе абстрактного суперкласса не выполнена реализация всех абстрактных методов, то подкласс по умолчанию также является абстрактным.Эквивалентность объектов
Для определения равенства различных объектов применяется метод equals.Метод equals реализован в классе Object и соответственно наследуем любым классом Java. Большинство классов переопределяет этот метод таким образом, что позволяет сравнивать на эквивалентность объекты конкретного класса.
Например:
class A { String objectName; A (String name) { objectName = name; } // Конструктор } public class MyA { public static void main (String args[ ]) { A A = new A("Class1"); // Создание экземпляра класса
A A_eq = A; // Ссылка на существующий объект
A A_clon = (A)A.clone; // Создание объекта методом clone
A A_2 = new A("Class2"); // Сравнение объектов: if (A.equals(A_eq)) { } if (A.equals(A_ clon)) { } if (A.equals(A_ 2)) { } } }
Выполнив приведенный пример, можно увидеть, что эквивалентными будут только экземпляр класса и объект, созданный как ссылка на данный экземпляр класса. А экземпляр класса и его клон, так же, как и различные экземпляры одного класса, не совпадают.
Принадлежность объекта к конкретному классу проверяется с помощью оператора instanseof.
Механизмы наследования
Класс - это шаблон, в котором определяются данные и поведение объекта. Объекты одного класса совместно используют общую структуру данных и общее поведение.Объявление класса в языке Java создает новый ссылочный тип, определяющий как описание методов, так и их реализацию.
Объявление интерфейса создает новый ссылочный тип, который специфицирует описание методов и имена некоторых констант, но не определяет саму их реализацию.
Интерфейс может быть объявлен для расширения одного или нескольких интерфейсов.
Наследование позволяет определять новые классы в терминах существующих классов.
В языке Java поддерживается только простое наследование: любой подкласс является производным только от одного непосредственного суперкласса. При этом любой класс может наследоваться от нескольких интерфейсов.
Наследование интерфейсов реализует некоторую замену множественному наследованию, когда вместо того чтобы один класс имел несколько непосредственных суперклассов, этот класс наследует несколько интерфейсов. Интерфейс позволяет определить некоторый шаблон класса: описание методов без их реализации.
Язык Java разрешает несколько уровней наследования, определяемых непосредственным суперклассом и косвенными суперклассами. Наследование можно использовать для создания иерархии классов.
При создании подкласса на основе одного или нескольких суперклассов возможны следующие способы изменения поведения и структуры класса:
Объявление интерфейса
Объявление интерфейса вводит новый ссылочный тип, членами которого являются константы и абстрактные методы.Реализация интерфейса всегда выполняется в классе, который использует данный интерфейс.
Один класс может реализовывать несколько интерфейсов, и один интерфейс может использоваться несколькими классами.
Интерфейсы позволяют разделять общую структуру - методы и константы, классами, не связанными между собой иерархией наследования.
Объявление интерфейса может иметь следующее формальное описание:
МодификаторИнтерфейса ИдентификаторИнтерфейса extends СписокНаследуемыхИнтерфейсов {ТелоИнтерфейса}
Самым простым примером объявления интерфейса может быть следующий код:
interface MyColors { int RED = 1, YELLOW = 2, BLUE = 4; }
Интерфейс может иметь в качестве предков только интерфейсы.
Интерфейс может иметь модификаторы public и abstract. Язык Java подразумевает, что каждый интерфейс по умолчанию имеет модификатор abstract, который не рекомендуется указывать явно.
К интерфейсу, объявленному с модификатором доступа public, может быть произведен доступ из других пакетов, в противном случае - только из своего пакета.
Тело интерфейса заключается в фигурные скобки и не может содержать конструктора или блоков инициализации статических переменных. В остальном тело конструктора идентично телу абстрактного класса.
Любая переменная интерфейса по умолчанию считается переменной с модификаторами public, static и final и обязательно должна быть инициализирована константным выражением. Также каждая переменная в теле интерфейса неявно считается переменной с модификатором.
Метод, объявленный в теле интерфейса, по умолчанию считается методом с модификаторами abstract и public. Объявление метода завершается точкой с запятой и не содержит тела метода, заключенного в фигурные скобки.
Метод интерфейса не может иметь модификаторов final или static.
Интерфейс можно использовать как ссылочный тип при объявлении переменных. Переменная или выражение типа интерфейса могут ссылаться на любой объект, который является экземпляром класса, реализующего данный интерфейс. Переменную типа интерфейса можно использовать только после присвоения ей ссылки на объект ссылочного типа, для которого был реализован данный интерфейс.
Объявление класса
Объявление класса вводит новый ссылочный тип и определяет или часть или всю его реализацию.При загрузке класса выделяется память для всех его статических переменных и затем выполняется их инициализация.
Объявление класса может иметь следующее формальное описание:
МодификаторыКласса class ИмяКласса extends ИмяСуперКласса implements СписокРеализуемыхИнтерфейсов {ТелоКласса}
Тело класса содержит описание переменных, методов и вложенных классов и заключается в фигурные скобки. В частном случае тело класса может не содержать ни одного объявления.
Например:
public class A implements B { public A() { } public int Metod1(){return 0;} } interface B { public int Metod1(); }
Вложенный класс описывается так же, как и внешний.
В описании каждого класса указывается имя класса и тело класса, содержащее объявления полей класса. Дополнительно для класса могут быть заданы модификаторы класса и указан непосредственный суперкласс и реализуемые классом интерфейсы. Полями класса в Java называются переменные и методы, определяемые в теле класса.
Класс может иметь более одного модификатора класса.
В языке программирования Java существуют следующие модификаторы класса:
Для указания модификаторов класса применяются следующие правила:
Такой файл называется модулем компиляции;
Любой класс может иметь только один суперкласс, указываемый ключевым словом extends. Наследуемый суперкласс должен быть доступным классом и не иметь модификатора final.
По умолчанию предполагается, что в том случае, если для класса не указано никакого суперкласса, то его непосредственным суперклассом станет класс Object из пакета java.lang, который всегда является корневым классом в любой формируемой иерархии классов.
Пакеты классов JDK
Инструментальный набор JDK включает утилиты и пакеты классов.Собственно говоря, все стандартные возможности программирования на Java базируются на классах этого инструментального набора. JDK состоит из нескольких отдельных пакетов, иногда называемых библиотеками. Различные среды программирования, такие как JBuilder, предоставляют дополнительные пакеты классов.
Среда JBuilder 8 Enterprise полностью поддерживает работу со всеми классами пакета JDK 1.4.

Приведение ссылочных типов
В языке Java преобразование ссылочных типов может происходить в четырех контекстах:Наиболее общим случаем является приведение.
В языке Java объявление класса определяет одноименный ссылочный тип. Все переменные, имеющие ссылочный тип, указываемый именем класса, являются объектами. Тип такого объекта определяется оператором instanceof.
Например:
String str1; Object ObjectName; ObjectName =(Object) str1; // Приведение типа
if (ObjectName instanceof String) { // Тип объекта String String str2 = (String) ObjectName ; System.out.println(str2); // Запись в стандартный поток // вывода значения строки, // указанной параметром str2 }
В рассмотренном примере тип объекта str1 сначала приводится к типу своего косвенного суперкласса Object, а затем оператором instanceof определяется непосредственный тип объекта (при определении оператором instanceof типа объекта ObjectName как String или Object возвращается значение true).
При приведении ссылочных типов действуют следующие правила:
После приведения объекта к типу суперкласса все переменные и методы самого класса объекта становятся недоступными для приведенного объекта.
Преобразование присвоением происходит в том случае, когда применяясь к переменной и значению, преобразует значение к типу переменной. При преобразовании присвоением не требуется проверки времени выполнения.
Значение простого типа не может быть присвоено значению ссылочного типа, как и значение ссылочного типа не может быть присвоено значению простого типа.
Присвоение значений ссылочного типа переменным ссылочного типа не требует во время выполнения действий преобразования. Просто компилятор должен иметь возможность преобразовать тип значения времени компиляции к типу переменной.
Например:
public class A { int x, y; } public class ADerived extends A { int z; } public interface B { void setColor(int color); } public class AB extends A implements B { int color; public void setColor(int color) { this.color = color; } } class MyA{ public static void main(String[ ] args) { // Создание переменных ссылочного типа // и присваивание им значения: A p = new A(); p = new ADerived(); // Правильно: ADerived это // подкласс класса A ADerived p3d = p; // Ошибка
// Присваивание значения переменной // типа Object: Object o = p; // Правильно: любой объект приводим // к типу Object int[ ] a = new int[3]; Object o2 = a; // Правильно: массив приводим к Object
// Присваивание значения переменной // типа интерфейса: AB cp = new AB(); B c = cp; // Правильно: AB реализует интерфейс B
// Присваивание значения переменной // типа массива: byte[ ] b = new byte[4]; a = b; // Ошибка: это массивы различных типов ADerived[ ] p3da = new ADerived[3]; A[ ] pa = p3da; // Правильно: ADerived подкласс класса A p3da = pa; // Ошибка, т.к. необходимо приведение } }
Преобразование приведением является более общим: если преобразование возможно, то приведение может его выполнить. Часто следует явно указывать преобразование приведением, чтобы компилятор имел строгую информацию о типе объекта. Для любого типа значения времени компиляции всегда можно выполнить приведение к тому же самому типу. Это позволит точно указать, что результирующее значение будет иметь именно указанный тип.
Приведением можно преобразовывать значения одного вычислимого типа к другому. Тип boolean не может быть приведен ни к какому другому типу.
Значения простых типов не могут быть приведены к значениям ссылочных типов и наоборот.
Раннее и позднее связывание
Раннее и позднее связывание является одним из проявлений полиморфизма, позволяя выполнять одним оператором вызов различных методов в зависимости от типа объекта.В следующем примере оператор b.MyPrint() будет вызывать различные методы в зависимости от типа объекта, для которого он выполняется:
class B{ void MyPrint () { }} class B1 extends B{ void MyPrint () { //Переопределение метода } } class B2 extends B{ void MyPrint () { //Переопределение метода } } class B3 extends B{ void MyPrint () { //Переопределение метода } } class PrintB{ B DoBObject () { // Метод, реализующий класс B B b; b= new B1(); … b= new B2(); … b= new B3(); … return b; } ... B b= new PrintB.DoBObject(); b.MyPrint() // Выполняет вызов метода MyPrint в // соответствии с типом объекта b
Вложенные классы
Проблемы, возникающие из-за отсутствия множественного наследования, частично можно решить с использованием вложенных классов.Если требуется, чтобы класс ClassA наследовал все доступные методы, включая и protected-методы, двух классов (класса ClassB и класса ClassC), то реализовать такую схему можно через вложенный класс.
Класс ClassA создается производным от класса ClassB. Это позволит всем экземплярам класса ClassA вызывать protected-методы класса ClassB.
В классе ClassA объявляется подкласс ClassFromC, производный от класса ClassC и переопределяющий все protected-методы класса ClassC, реализуя их как вызовы соответствующих методов из суперкласса.
При такой реализации экземпляр класса ClassA получает доступ к protected-методам двух различных классов - своего суперкласса и своего вложенного класса.
Введение в программирование
Конструкторы
Конструктором называется метод, вызываемый для инициализации объекта при его создании.Имя конструктора всегда совпадает с именем класса. Конструктор не может использовать оператор return и для него не указывается никакого типа возвращаемого значения. При объявлении конструктора можно указать модификаторы доступа public, protected или private.
Наличие явно описанного конструктора в классе не является обязательным. В этом случае при создании объекта используется конструктор по умолчанию. Такой конструктор не получает параметров и вызывает непосредственно конструктор суперкласса: super().
При этом, если суперкласс не имеет конструктора без списка параметров, то происходит ошибка компиляции.
Конструкторы не наследуются подклассами.
Тело конструктора заключается в фигурные скобки.
При этом первыми операторами должны быть указаны, если они используются, операторы вызова конструкторов данного класса или суперкласса.
Конструктор может иметь следующее формальное описание:
ИмяКласса (СписокПараметров) { ВызовКонструкторов БлокОператоров }
Вызов конструкторов выполняется операторами this и super с указанием в скобках списка параметров.
Например:
public class C extends A { public C() {this(1,20); } // Вызов конструктора данного класса public C(int i, int j) { } }
Вызов конструкторов подчиняется следующим правилам:
Объявление переменных
В языке Java при объявлении переменной - указании ее типа и имени - одновременно можно выполнить ее инициализацию.Для переменной могут быть указаны следующие модификаторы доступа:
Переменная, не имеющая модификатора static, называется переменной экземпляра. Переменная экземпляра создается для каждого нового экземпляра класса.
Переменная, имеющая модификатор static, называется переменной класса. Для всех экземпляров класса существует только одна переменная класса. Эта переменная доступна и до создания экземпляра класса.
Переменная, имеющая модификатор final, должна быть инициализирована внутри тела класса, так как вне тела класса final-переменная является константой и ее изменение или инициализация запрещены.
Модификатор transient нельзя использовать совместно с модификаторами final или static.
Любая переменная может иметь модификатор доступа public (общедоступный), protected (защищенный) или private (скрытый).
Для этих модификаторов определены следующие правила доступа:
По умолчанию переменная считается доступной везде внутри пакета, содержащего класс, в котором она объявлена, и не недоступна ни в каком другом подклассе указанного класса в том случае, если подкласс содержится в другом пакете.
Инициализация переменной означает присвоение этой переменной некоторого значения. В качестве значения инициализации могут выступать:
Если переменная объявлена с модификатором static, то инициализация выполнятся сразу при загрузке класса.
Если объявляемая переменная является переменной экземпляра, то инициализация выполняется при выполнении метода-конструктора класса.
Если объявляемая переменная является локальной переменной, то ее инициализация происходит при выполнении оператора объявления этой переменной.
Например:
int i1,i2; float f1 = 3.0; float dArray1 [] = new float [14]; java.lang.String str1 = "no?iea"; Object oObj = str1 ; Exception e = new Exception ( ) ;
Определение методов
В языке Java определение метода включает его объявление и реализацию. Определение метода всегда указывается в теле класса.Метод может иметь модификаторы доступа, возвращать значение и получать параметры.
Метод может иметь следующие модификаторы:
Для модификаторов доступа метода определены следующие правила:
По умолчанию метод считается доступным везде внутри пакета, содержащего класс, в котором он определен, и недоступным ни в каком другом подклассе указанного класса в том случае, если подкласс содержится в другом пакете.
Метод, не имеющий модификатора static, называется методом экземпляра. Метод экземпляра может быть вызван только для созданного экземпляра класса или подкласса. Такой метод нельзя вызывать непосредственно, квалифицируя его именем класса.
Метод, объявленный с модификатором static, называется статическим методом (или методом класса) и может быть вызван без создания экземпляра класса. Этот метод всегда вызывается непосредственно из класса. Статический метод имеет доступ к другим статическим переменным и методам данного класса.
Если статический метод определен как final-метод, то он не может быть переопределен.
Например:
// Файл A.java package classa; public class A implements B { public A() { } static int b=1; public int Metod1(){return a;} public static int Metod2(){ return 0;} //Статический метод } interface B { final public static int a=1; // Статическая переменная int Metod1(); }
// Файл C.java package classa; public class C extends A { public C() { } static int b=3; public int Metod1(){return a;} public static int Metod2(){return 77;} public static void main(String[] args) { System.out.println(A.Metod2()); System.out.println(C.Metod2()); } }
При переопределении методов их модификаторы доступа должны совпадать. Так, нельзя переопределить метод, имеющий модификатор доступа public, методом с модификатором доступа private.
Абстрактный метод указывается модификатором abstract. Такой метод никогда не имеет тела метода: вместо фигурных скобок, ограничивающих тело метода, объявление метода завершается точкой с запятой.
Абстрактные методы можно объявлять только в абстрактных классах или интерфейсах. Объявление абстрактного метода в классе, не имеющем модификатора abstract, приводит к ошибке компиляции. Любой подкласс абстрактного класса, который сам не является абстрактным классом, должен определять реализацию всех наследуемых не реализованных абстрактных методов.
Например:
public class A extends AA implements B { public A() { } public int Metod1(){return 0;} public static int Metod2(){return 0;} int Metod3(){return 0;} public int Metod4(){return 0;} } interface B { int Metod1(); abstract int Metod4(); } abstract class AA{ abstract int Metod3(); }
Методы, объявленные с модификатором private, не могут быть абстрактными методами, так как они недоступны вне тела класса. Статические методы также не могут выступать в качестве абстрактных методов, так как считаются конечными и не могут быть переопределены.
Объявление метода с модификатором final запрещает его последующее переопределение. Такие методы называются конечными методами, и по умолчанию считается, что private-метод всегда является конечным методом.
Методы, объявленные с модификатором native, могут иметь реализацию на другом языке программирования. Эти методы используются для написания машинно-зависимого кода. native-методы в Java-программе не имеют тела метода.
Synchronized-методы выполняются с блокировкой:
Это позволяет предотвратить параллельный доступ к данным из различных потоков в многопоточном приложении.
Метод может возвращать значение заданного типа. В этом случае:
Если метод не имеет возвращаемого значения, то он должен быть объявлен с ключевым словом void, указывающим на отсутствие возвращаемого значения.
Если в качестве возвращаемого значения должен быть использован массив, то после имени метода может быть указана пара квадратных скобок или список параметров и пара квадратных скобок.
Передача параметров
Список формальных параметров содержит упорядоченные и разделенные запятой описания параметров (тип параметра и идентификатор параметра). Если метод не имеет параметров, передаваемых ему при вызове, то после имени метода указывается пара пустых скобок.В языке Java все параметры передаются по значению. При передаче параметра ссылочного типа его значением является ссылка на объект, поэтому в метод передается ссылка на экземпляр класса.
Параметры в языке Java рассматриваются как локальные переменные метода, специфицированные вне тела метода. При вызове метода перед его выполнением происходит вычисление выражений, представляющих фактические передаваемые методу параметры, и присвоение вычисленных значений соответствующим локальным переменным (формальным параметрам).
Перегружаемые методы
Перегружаемыми методами называется группа методов с одинаковым именем, но различными списками параметров. Язык Java позволяет определять в классах перегружаемые методы.Переопределение метода
Переопределением метода называется объявление в подклассе метода, у которого имя, тип и список параметров, в точности совпадают с соответствующими атрибутами метода, ранее объявленного в суперклассе.При переопределении метода использование модификаторов доступа должно удовлетворять следующим условиям:
Тип возвращаемого значения переопределяющего метода должен совпадать или преобразовываться присвоением к типу возвращаемого значения переопределяемого метода.
Если класс содержит переопределяющий метод, то переопределенный метод можно вызвать только с квалификацией ссылки ключевым словом super или именем суперкласса (для статических членов).
Например:
class A { public void Metod1() { } public static void Metod2() { }
} class B extends A { public Metod1() { } ... Metod1(); // Вызов метода из класса B super.Metod1(); // Вызов метода из класса A A.Metod2(); }
Создание объекта
При создании объекта последовательно выполняются следующие действия: вызывается конструктор, создается новый объект указанного типа, и все переменные экземпляра инициализируются своими значениями по умолчанию, затем формируется ссылка на созданный и инициализированный объект. Эта ссылка и является значением выражения, создающего объект.Объект может быть создан вызовом метода newInstance. В этом случае используется конструктор без списка параметров.
Ссылка на текущий объект
При вызове метода экземпляра ключевое слово thisКлючевое слово super используется для указания квалифицированной ссылки на поля (переменные и методы) суперкласса.
По умолчанию любой метод первоначально ссылается на свои собственные переменные и методы и только в случае их отсутствия выполняется поиск этих полей вверх по иерархии суперклассов. При этом, если метод не переопределяется, то его имя не обязательно квалифицировать.
Например:
class A { public int a; } class B extends A { public int a, b; public void Metod1 ( ) { b=a; // Ссылка на this.a b=super.a; } }
Имя суперкласса может быть использовано для квалифицированного доступа к статическим переменным или методам.
Статические переменные
Для каждого класса создается только один экземпляр статической переменной (переменой класса).При описании статических переменных должны быть выполнены следующие правила:
Инициализация статических переменных выполняется при загрузке класса. Инициализация переменных выполняется в порядке их следования в объявлении класса.
Например:
public class C { public C() { } static int b=3; static int c; static { b++; c = 10; } // Инициализация статической переменной }
При инициализации статических переменных можно вызывать статические методы.

Введение в программирование
public static int myPublicInt; internal
|
using System; namespace MyNameSpace { public class A { public static int myPublicInt; internal static int myInternalInt; private static int myPrivateInt = 0; public class Nest1 // "Вложенный" член класса { public static int myPublicInt; internal static int myInternalInt; private static int myPrivateInt = 0; } private class Nest2 // "Вложенный" член класса { public static int myPublicInt = 0; internal static int myInternalInt = 0; private static int myPrivateInt = 0; } } public class MyClass { public static int Main() { // Доступ к членам класса A: A.myPublicInt = 1; // Доступ не ограничен A.myInternalInt = 2; // Только в текущем проекте // A.myPrivateInt = 3; - ошибка: нет // доступа вне класса // Доступ к членам класса Nest1: A.Nest1.myPublicInt = 1; // Доступ не ограничен A.Nest1.myInternalInt = 2; // Только в текущем проекте // A.Nest1.myPrivateInt = 3; - ошибка: нет // доступа вне класса Nest1 // Доступ к членам класса Nest2: // A.Nest2.myPublicInt = 1; - ошибка: нет // доступа вне класса A // A.Nest2.myInternalInt = 2; - ошибка: нет // доступа вне класса A // A.Nest2.myPrivateInt = 3; - ошибка: нет // доступа вне класса Nest2 return 0; } } } |
| Листинг 15.1. |
| Закрыть окно |
using System;
namespace MyNameSpace
{
public class A
{
public static int myPublicInt;
internal static int myInternalInt;
private static int myPrivateInt = 0;
public class Nest1 // "Вложенный" член класса
{
public static int myPublicInt;
internal static int myInternalInt;
private static int myPrivateInt = 0;
}
private class Nest2 // "Вложенный" член класса
{
public static int myPublicInt = 0;
internal static int myInternalInt = 0;
private static int myPrivateInt = 0;
}
}
public class MyClass
{
public static int Main()
{
// Доступ к членам класса A:
A.myPublicInt = 1; // Доступ не ограничен
A.myInternalInt = 2; // Только в текущем проекте
// A.myPrivateInt = 3; - ошибка: нет
// доступа вне класса
// Доступ к членам класса Nest1:
A.Nest1.myPublicInt = 1; // Доступ не ограничен
A.Nest1.myInternalInt = 2; // Только в текущем проекте
// A.Nest1.myPrivateInt = 3; - ошибка: нет
// доступа вне класса Nest1
// Доступ к членам класса Nest2:
// A.Nest2.myPublicInt = 1; - ошибка: нет
// доступа вне класса A
// A.Nest2.myInternalInt = 2; - ошибка: нет
// доступа вне класса A
// A.Nest2.myPrivateInt = 3; - ошибка: нет
// доступа вне класса Nest2
return 0;
}
}
}
using System; public class MyClass1
|
using System; public class MyClass1 { public static void UseParams1(params int[] list) { // Отображение списка параметров for ( int i = 0 ; i < list.Length ; i++ ) Console.WriteLine(list[i]); } public static void UseParams2(params object[] list) { // В переменном списке параметров могут быть // объекты различных типов for ( int i = 0 ; i < list.Length ; i++ ) Console.WriteLine((object)list[i]); } public static void UseParams3(int k,params object[] list) { // В переменный список параметров // включаются параметры, начиная // со второго for ( int i = 0 ; i < list.Length ; i++ ) Console.WriteLine((object)list[i]); } public static void Main() { UseParams1(1, 2, 3, 4, 5); UseParams1(1, 2); int[] myarray = new int[3] {1,2,3}; UseParams1(myarray); UseParams2(111, 'f', "string"); UseParams3(111, 'f', "string"); } } |
| Листинг 15.2. |
| Закрыть окно |
using System;
public class MyClass1
{
public static void UseParams1(params int[] list)
{
// Отображение списка параметров
for ( int i = 0 ; i < list.Length ; i++ )
Console.WriteLine(list[i]);
}
public static void UseParams2(params object[] list)
{ // В переменном списке параметров могут быть
// объекты различных типов
for ( int i = 0 ; i < list.Length ; i++ )
Console.WriteLine((object)list[i]);
}
public static void UseParams3(int k,params object[] list)
{ // В переменный список параметров
// включаются параметры, начиная
// со второго
for ( int i = 0 ; i < list.Length ; i++ )
Console.WriteLine((object)list[i]);
}
public static void Main()
{
UseParams1(1, 2, 3, 4, 5);
UseParams1(1, 2);
int[] myarray = new int[3] {1,2,3};
UseParams1(myarray);
UseParams2(111, 'f', "string");
UseParams3(111, 'f', "string");
}
}
Данный пример иллюстрирует консольное приложение,
|
/* Данный пример иллюстрирует консольное приложение, позволяющее добавлять и отображать элементы структуры. Используемый массив myAB содержит 400 элементов, каждый из которых является структурой из двух полей - name и telfax*/ using System; namespace MyStruct1 { struct AddrBookType { public string name; public string telfax ; } /* Созданная структура определяет новый структурный тип AddrBookType. Элементы структуры объявлены с модификаторами доступа public, так как по умолчанию элементы доступны только внутри структуры */ class Class1 { static void Main(string[] args) { // Создание массива структур AddrBookType[] myAB= new AddrBookType[400]; char icount; char iloop; int i=0; int j=0; while (true) // Запрос кода операции {Console.Write ("Insert kod (0 - new record, 1 - show all, 2 - exit): "); icount = (char)Console.Read (); while (true) // Чтение потока ввода { iloop = (char)Console.Read (); /* Цикл чтения символов будет завершен при нажатии пользователем клавиши Enter и получения из потока ввода символа '\n' */ if (iloop == '\n') break;} if (icount=='2') break; switch (icount) {case '0': Console.WriteLine("Insert Name: "); myAB[i].name =Console.ReadLine (); Console.WriteLine("Insert phone : "); myAB[i].telfax= Console.ReadLine (); i++; // Счетчик введенных элементов break; case '1': // Запись в стандартный поток // вывода for ( j=0; j |
| Листинг 15.3. |
| Закрыть окно |
/* Данный пример иллюстрирует консольное приложение, позволяющее добавлять и отображать элементы структуры. Используемый массив myAB содержит 400 элементов, каждый из которых является структурой из двух полей - name и telfax*/
using System;
namespace MyStruct1
{
struct AddrBookType
{ public string name;
public string telfax ;
}
/* Созданная структура определяет новый структурный тип AddrBookType. Элементы структуры объявлены с модификаторами доступа public, так как по умолчанию элементы доступны только внутри структуры */
class Class1
{
static void Main(string[] args)
{ // Создание массива структур
AddrBookType[] myAB= new AddrBookType[400];
char icount;
char iloop;
int i=0;
int j=0;
while (true) // Запрос кода операции
{Console.Write ("Insert kod (0 - new record,
1 - show all, 2 - exit): ");
icount = (char)Console.Read ();
while (true) // Чтение потока ввода
{ iloop = (char)Console.Read ();
/* Цикл чтения символов будет завершен при
нажатии пользователем клавиши Enter и
получения из потока ввода символа '\n' */
if (iloop == '\n') break;}
if (icount=='2') break;
switch (icount)
{case '0':
Console.WriteLine("Insert Name: ");
myAB[i].name =Console.ReadLine ();
Console.WriteLine("Insert phone : ");
myAB[i].telfax= Console.ReadLine ();
i++; // Счетчик введенных элементов
break;
case '1': // Запись в стандартный поток
// вывода
for ( j=0; j
{Console.Write(myAB[j].name);
Console.Write(" ");
Console.WriteLine(myAB[j].telfax); }
break;
default :
Console.WriteLine("Ошибка ввода");
break;
} // Конец switch
} // Конец while
}
} }
Явный вызов конструктора
Определение конструктора может содержать явный вызов конструктора того же класса. Вызываемый конструктор указывается после имени определяемого конструктора со списком параметров через символ двоеточия. Вызываемый конструктор может быть определен ключевым словом this - для вызова конструктора из того же самого класса, или ключевым словом base - для вызова конструктора базового класса. Явно вызываемый конструктор будет выполнен до выполнения конструктора, в котором он указывается.Например:
public class A { public A():this(222) // Конструктор без параметров { } public A(int i) // Конструктор с одним параметром { } }
Комментарии в программе на языке C#
Комментарий в языке С# может быть как однострочным, так и многострочным.Однострочный комментарий может размещаться в начале строки или после некоторого кода. Он начинается символами // и завершается концом строки.
Многострочный комментарий располагается между парами символов /* и */ .
Комментарий, вставляемый средой проектирования, например // TODO: Add code to start application here указывает место, в которое должен быть вставлен код, выполняемый при запуске приложения.
Существует особый тип комментария, который записывается в summary-секции:
///
Такой комментарий может быть использован для автоматического документирования приложения.
При автоматическом документировании приложения создается XML-файл, представляющий собой информацию о приложении как некоторую иерархию секций. Для того чтобы при компиляции приложения создавался XML-файл документа, следует установить опции компиляции следующим образом: в окне Solution Explorer выделить секцию с именем проекта и выполнить команду меню View|Property Pages (или Shift+F4), а затем, выбрав папку Configuration Properties и страницу свойств Build, установить новое значение свойства XML Documentation File, описывающее имя файла, в котором будет сохранен XML-документ.
Методы члены класса
Среда проектирования Visual Studio .NET дает возможность использовать мастер создания метода - члена класса (в окне Class View выбрать имя класса и выполнить команду контекстного меню Add|Add Metod). Список Modifier. диалога. C# Metod Wizard позволяет указать один из следующих модификаторов параметра метода:Например:
public void Metod1(int i, ref int j, out int k) { }
При обращении к методу или полю - членам класса используется операция . (точка) - доступ к члену класса. Имя поля или метода члена класса квалифицируется именем экземпляра класса.
Язык C# позволяет использовать методы с переменным числом параметров. Для метода с переменным числом параметров должны быть выполнены следующие правила:
Количество параметров в переменном списке параметров определяется свойством Length .
Например:
using System; public class MyClass1 { public static void UseParams1(params int[] list) { // Отображение списка параметров for ( int i = 0 ; i < list.Length ; i++ ) Console.WriteLine(list[i]); } public static void UseParams2(params object[] list) { // В переменном списке параметров могут быть // объекты различных типов for ( int i = 0 ; i < list.Length ; i++ ) Console.WriteLine((object)list[i]); } public static void UseParams3(int k,params object[] list) { // В переменный список параметров // включаются параметры, начиная // со второго for ( int i = 0 ; i < list.Length ; i++ ) Console.WriteLine((object)list[i]); } public static void Main() { UseParams1(1, 2, 3, 4, 5); UseParams1(1, 2); int[] myarray = new int[3] {1,2,3}; UseParams1(myarray); UseParams2(111, 'f', "string"); UseParams3(111, 'f', "string");
} }
Листинг 15.2.
Модификаторы доступа
В языке C# применяются следующие модификаторы доступа:Для любого члена класса или объектного типа разрешено указывать только один модификатор доступа, за исключением комбинации protected internal, регламентирующей ограничение доступа наследуемыми классами текущего проекта.
Например:
class A { protected int x = 100; }
class B : A { void M1() { A a1 = new A(); // Создание объекта типа A B b1 = new B(); // Создание объекта типа B // a1.x = 200; - доступ не разрешен b1.x = 200; // Правильно реализованный доступ } }
Отметим, что пространство имен не может иметь модификатора доступа.
Язык C# поддерживает использование вложенных классов.
Типы верхнего уровня, которые не являются вложенными в другие типы, могут иметь модификатор доступа только internal (по умолчанию) или public.
Если модификатор доступа не указан, то применяется доступ по умолчанию. В следующей таблице отображены модификаторы доступа для вложенных типов, являющихся членами других типов.
| enum | Public | - | |
| class | private | public protected internal private protected internal | |
| interface | public | - | |
| struct | private | public internal private Структуры не могут иметь модификатор доступа protected,так как не могут быть наследуемы |
Например:
using System; namespace MyNameSpace { public class A { public static int myPublicInt; internal static int myInternalInt; private static int myPrivateInt = 0;
public class Nest1 // "Вложенный" член класса { public static int myPublicInt; internal static int myInternalInt; private static int myPrivateInt = 0; }
private class Nest2 // "Вложенный" член класса { public static int myPublicInt = 0; internal static int myInternalInt = 0; private static int myPrivateInt = 0; } } public class MyClass { public static int Main() { // Доступ к членам класса A: A.myPublicInt = 1; // Доступ не ограничен A.myInternalInt = 2; // Только в текущем проекте // A.myPrivateInt = 3; - ошибка: нет // доступа вне класса // Доступ к членам класса Nest1: A.Nest1.myPublicInt = 1; // Доступ не ограничен A.Nest1.myInternalInt = 2; // Только в текущем проекте // A.Nest1.myPrivateInt = 3; - ошибка: нет // доступа вне класса Nest1 // Доступ к членам класса Nest2: // A.Nest2.myPublicInt = 1; - ошибка: нет // доступа вне класса A // A.Nest2.myInternalInt = 2; - ошибка: нет // доступа вне класса A // A.Nest2.myPrivateInt = 3; - ошибка: нет // доступа вне класса Nest2 return 0; } } }
Листинг 15.1.
Объявление класса
В языке С# определение класса не обязательно должно иметь методы конструктор и деструктор.Управляемый код на языке С# избавлен от необходимости освобождения памяти, выделяемой под объекты, так как это реализуется средой NET Framework. Поэтому основное назначение деструктора в языке C# - это освобождение неуправляемых ресурсов, таких как окна, файлы, сетевые соединения и т.п.
Язык C# поддерживает три типа конструкторов:
Конструктор экземпляра объекта имеет следующее формальное описание:
[атрибуты] [модификаторы_доступа] имя_конструктора([список_формальных_параметров]) [:base (список_аргументов) | :this (список_аргументов)] { тело_конструктора }
Ключевое слово base определяет явный вызов конструктора базового класса, а ключевое слово this - вызов конструктора данного класса с указанным списком параметров.
Например:
public class AClass1 { public AClass1()// Объявление конструктора { } }
Ключевое слово class определяет имя объявляемого класса. Тело объявляемого класса указывается в фигурных скобках.
Ключевое слово public - это модификатор доступа, указывающий, что объявляемые после него идентификаторы (имена классов или методов) будут общедоступны (модификатор доступа позволяет определить область видимости переменных и методов - членов класса).
По умолчанию все переменные и методы - члены класса, заданные без модификатора доступа, считаются private-переменными (называемыми иногда приватными или закрытыми). Приватные переменные доступны только внутри экземпляра класса и не могут быть использованы во внешних функциях модуля.
Пространство имен System
Библиотека классов .NET Framework среды Visual Studio.NET состоит из иерархически организованного набора пространства имен. В каждом пространстве имен определяется набор типов (классы, структуры, нумераторы, интерфейсы). Пространство имен System содержит набор классов для общеиспользуемых значений и ссылочных типов данных, событий и обработчиков событий, интерфейсов, атрибутов и т.п. Также это пространство имен содержит классы, позволяющие выполнять преобразование типов, реализовывать операции ввода/вывода и т.п.Все встроенные типы данных языка C# реализованы как классы пространства имен. Пространство имен System включает такие классы, как Console, String, Array, Math, Boolean, Byte, Char, DateTime, Decimal, Double, Int16, Int32, Voidи т.п.
Псевдонимы типов языка C# и соответствующие им предопределенные типы пространства имен являются при написании программы взаимозаменяемыми.
Для определения типа переменной можно использовать метод GetType или оператор typeof.
Библиотека классов NET Framework предоставляет для реализации потоков ввода, вывода и ошибок класс Console, располагаемый в пространстве имен System.
Класс Console имеет следующие свойства, описывающие соответствующие потоки ввода/вывода:
Класс Console содержит следующие методы, позволяющие осуществлять чтение и запись символов из потоков ввода/вывода:
Работа с различными видами коллекций реализуется такими классами пространства имен System.Collections , как:
Пространство имен
Пространство имен позволяет именовать группу данных, таких как классы, переменные и/или методы. В языке C# все библиотеки классов подключаются как пространства имен.При автоматическом формировании проекта в среде Visual Studio.NET первой строкой создаваемого приложения вставляется строка using System.
Ключевое слово using подключает библиотеку классов System (каждая библиотека классов рассматривается как пространство имен).
Создание пространства имен указывается ключевым словом namespace.
Объявляемые пространства имен могут использоваться для структурирования программы.
Например:
namespace NameSN1.NameSN2 { class A {} } namespace NameSN3 { using NameSN1.NameSN2; class B: A {} }
В среде проектирования Visual Studio.NET библиотеки классов NET Framework образуют иерархическую структуру пространств имен.
Библиотеку классов среды .NET Framework иногда называют NET Framework-библиотекой или просто Framework-библиотекой.
Объявление пространства имен имеет следующее формальное описание:
namespace name[.name1] ...] { // объявляемые_данные }
Пространство имен указывается идентификатором, который может содержать операцию . , определяющую составное имя пространства имен.
Объявляемыми данными пространства имен могут быть:
Для того чтобы иметь возможность обращаться к переменным или методам из пространства имен, можно использовать один из следующих способов:
Например:
System.Console.WriteLine("Печать строки");
Например:
using System;
Директива using может использоваться для:
Объявление псевдонима имеет следующее формальное описание:
using alias=class_name;
Например:
using System.Console = my_SN; class MyClass { public static void Main() { my_SN.WriteLine("123");} }
Директива using позволяет не квалифицировать каждую переменную пространством имен, а просто подключить требуемое пространство имен.
Создание экземпляра класса
Для использования переменных или методов класса следует создать объект - экземпляр класса.В языке C# экземпляр класса всегда создается при помощи оператора new. Если класс имеет несколько конструкторов, то при создании переменной указывается требуемый конструктор.
Место выделения памяти под объект зависит от типа создаваемого объекта: объекты ссылочных типов размещаются в куче, а объекты размерных типов - в стеке.
Объявление переменной объектного типа в языке С# не создает объекта, а только определяет идентификатор указанного типа. Обратите внимание, что во многих объектно-ориентированных языках, таких как С++, объявление переменной объектного типа также становится и созданием экземпляра данного типа.
Например:
using System; namespace MyNS { public class A { public A() // Конструктор без параметров { Console.WriteLine("A()"); } public A(int i) // Конструктор с одним параметром { Console.Write("A(i) i= "); Console.WriteLine(i); } } } using System; namespace MyNS { class MyClass { static void Main(string[] args) { A acl1= new A(); // Создание экземпляра класса A acl2= new A(987); } } }
Структура приложения на языке С#
Проектом называется совокупность файлов, содержащих информацию об установках, конфигурации, ресурсах проекта, а также файлов исходного кода и заголовочных файлов.Интегрированная среда проектирования VisualStudio.NET позволяет для создания проектов на разных языках программирования использовать различные инструментальные средства проектирования (например, Microsoft Visual Basic, Microsoft Visual C#).
Любое приложение на языке C#, разрабатываемое в среде проектирования VisualStudio.NET, реализуется как отдельный проект. Приложение на языке С# может состоять из нескольких модулей. Каждый модуль C# может содержать код нескольких классов (при создании приложения в среде Visual Studio.NET каждый класс C# автоматически помещается в отдельный модудь - файл с расщирением cs). Среда Visual Studio 2005 позволяет создавать частичные классы ( partial class), когда один класс содержится в нескольких файлах. Соединение всех частей класса выполняется на этапе компиляции. (Вообще, частичными также могут быть структуры и интерфейсы.)
Для консольного приложения один из классов, реализуемых модулем, должен содержать метод Main. В языке C# нет аппарата заголовочных файлов, используемого в языке С++, поэтому код модуля должен содержать как объявление, так и реализацию класса.
По умолчанию весь код класса, представляющего консольное приложение, заключается в одно пространство имен, одноименное с именем приложения.
Точкой входа в программу на языке C# является метод Main.
Этот метод может записываться как без параметров, так и с одним параметром типа string. - указателем на массив строк, который содержит значения параметров, введенных при запуске программы. В отличие от списка параметров, задаваемых при запуске С-приложения, список параметров C#-приложения не содержит в качестве первого параметра имя самого приложения.
Код метода указывается внутри фигурных скобок:
static void Main(string[] args) { }
Ключевое слово static определяет, что метод Main является статическим методом, вызываемым без создания экземпляра объекта типа класса, в котором этот метод определен.
Метод, не возвращающий никакого значения, указывается с ключевым словом void. Однако, метод Main может возвращать значение типа int.
Например:
static int Main(string[] args) { // Проверка числа введенных параметров if (args.Length == 0) { Console.WriteLine("Нет параметров"); return 1; } // Для получения значения параметра как значения типа long // используется функция Parse long num = Int64.Parse(args[0]); // Тип long языка C# является псевдонимом типа Int64. // Поэтому предыдущая запись эквивалентна записи // long num = long.Parse(args[0]); // Для получения значения параметра как значения // определенного типа также можно использовать // метод ToInt64 класса Convert long num = Convert.ToInt64(s); }
Компилятор C# допускает наличие метода Main сразу в нескольких классах. Но для успешной компиляции и выполнения такого приложения следует указать класс, метод Main которого следует считать точкой входа в приложение. Это можно сделать, установив опции проекта или указав опцию компилятора /main с именем класса, чей метод Main будет задействован (например: csc class1.cs class2.cs /main:Class2).
Для того чтобы установить опцию проекта, определяющую "стартовый" класс (Startup Object), следует открыть диалог свойств проекта Property Pages, в секции Common Properties на странице General выбрать опцию Startup Object и указать для нее имя "стартового" класса (рис. 1).

Рис. 15.1. Определение имени "стартового класса"
Структуры
Структуры языка C# идентичны классам и представляют поименованную совокупность компонент, называемых членами или элементами структуры. Элементом структуры может быть переменная любого допустимого типа или функция.Однако структура относится к размерному типу, а класс - к ссылочному типу. Под структуру выделяется память, достаточная для размещения всех элементов структуры.
При создании массивов применение типа структуры бывает более эффективно, чем применение типа класса.
Тип struct является размерным типом, который может содержать конструкторы, константы, поля, методы, свойства, индексаторы, операторы и вложенные типы.
Объявление структуры может иметь следующее формальное описание (необязательные элементы указаны в квадратных скобках):
[атрибуты] ]модификаторы] struct имя_структуры [:интерфейсы] { конструктор () {} тип_элемента_структуры имя_ элемента1; тип_элемента_структуры имя_ элемента2; ... тип_элемента_структуры имя_ элементаN; } [ ;] // имя_элемента может быть именем переменной или именем функции
Для обращения к отдельным элементам структуры используется оператор . (точка).
Доступ к элементам структуры может иметь следующее формальное описание:
struct имя_структуры { модификатор тип_элемента элемент_структуры1; модификатор тип_элемента элемент_структуры2; } // Объявление переменной типа структуры имя_структуры имя_переменной_структуры1; имя_структуры [] имя_переменной_структуры2 = new имя_структуры[размерность]; // Доступ к элементу структуры имя_переменной_структуры1.элемент_структуры1= значение1; имя_переменной_структуры2[индекс].элемент_структуры1=значение2;
Например:
/* Данный пример иллюстрирует консольное приложение, позволяющее добавлять и отображать элементы структуры. Используемый массив myAB содержит 400 элементов, каждый из которых является структурой из двух полей - name и telfax*/ using System; namespace MyStruct1 { struct AddrBookType { public string name; public string telfax ; } /* Созданная структура определяет новый структурный тип AddrBookType. Элементы структуры объявлены с модификаторами доступа public, так как по умолчанию элементы доступны только внутри структуры */ class Class1 { static void Main(string[] args) { // Создание массива структур AddrBookType[] myAB= new AddrBookType[400]; char icount; char iloop; int i=0; int j=0; while (true) // Запрос кода операции {Console.Write ("Insert kod (0 - new record, 1 - show all, 2 - exit): "); icount = (char)Console.Read (); while (true) // Чтение потока ввода { iloop = (char)Console.Read (); /* Цикл чтения символов будет завершен при нажатии пользователем клавиши Enter и получения из потока ввода символа '\n' */ if (iloop == '\n') break;} if (icount=='2') break; switch (icount) {case '0': Console.WriteLine("Insert Name: "); myAB[i].name =Console.ReadLine (); Console.WriteLine("Insert phone : "); myAB[i].telfax= Console.ReadLine (); i++; // Счетчик введенных элементов break; case '1': // Запись в стандартный поток // вывода for ( j=0; j Листинг 15.3.
Язык C# позволяет создавать переменные типы структуры двумя способами:

Управляемый код
Язык программирования C# был разработан как язык, использующий технологию .NET. Поэтому приложение на C# компилируется в промежуточный код, называемый IL-кодом (Intermediate Language) или MSIL-кодом (Microsoft intermediate language). Такой код перед выполнением компилируется JIT-компилятором в команды, отвечающие специфике конкретного процессора.Выполнение IL-кода управляется механизмом CLR (Common Language Runtime), непосредственно осуществляющим JIT-компиляцию (Just In Time), наложение политик безопасности, предоставление отладочных сервисов, автоматическое управление памятью (реализация механизма "сборки мусора"). IL-код можно компилировать как при установке приложения, так и при его выполнении (при этом компилироваться будет не весь код, а только реально вызываемые методы). В процессе компиляции кроме IL-кода формируются также метаданные, описывающие классы. CLR использует метаданные для поиска и загрузки классов, размещения объектов в памяти, определения входящих в класс методов и свойств. CLR можно рассматривать как некоторую виртуальную машину, выполняющую приложения .NET. Среда CLR обеспечивает единообразное поведение всех приложений вне зависимости от языка реализации кода. CLS-спецификация (Common Language Specification) определяет требования к CLS-совместимым языкам программирования, использующим классы .NET Framework как некоторую унифицированную систему типов.
Кроме создания MSIL-кода, CLS-совместимый компилятор добавляет в выходной EXE-файл метаданные, описывающие используемые типы и методы. Под метаданными понимаются некоторые данные, которые описывают другие данные. Используя метаданные, среда CLR определяет требуемые во время выполнения типы и методы.
Библиотеки классов .NET Framework предоставляют большой набор методов отражения, применяемых средой CLS и другими приложениями для получения информации о типах и методах, реализуемых приложением. Отражением называется механизм получения метаданных. АPI отражения среды .NET представляет собой иерархию классов System.Reflection.
.NET Framework - это та часть платформы .NET, которая предназначена для создания надежных, масштабируемых и распределенных приложений, использующих технологии .NET.
.NET Framework состоит из CLR (Common Language Runtime) - среды времени выполнения кода, и из набора библиотек классов (иногда называемых BCL - Base Class Library).
Приложения, выполняемые под управлением CLR, называются управляемым кодом.
В Windows выполняемым приложением является EXE-файл, библиотеки реализуются как DLL-файлы. Технология .NET предоставляет приложения в виде сборок, описывающих "логические" EXE- или DLL- файлы. Сборка содержит декларацию, которая описывает ее содержимое. Так, сборка mscorlib.dll содержит все стандартные пространства имен из пространства имен System для .NET.
Введение в программирование
Абстрактные классы
Абстрактным классом называется класс, который содержит один или несколько абстрактных методов.Абстрактный класс не может использоваться для создания объектов.
Как правило, абстрактный класс описывает некий интерфейс, который должен быть реализован всеми его производными классами.
Абстрактный метод языка C# не имеет тела метода и аналогичен чисто виртуальному методу языка C++.
Например:
public abstract int M1(int a, int b);
Абстрактный класс можно использовать только как базовый для других классов. При этом если производный класс не содержит реализации абстрактного метода, то он также является абстрактным классом.
По умолчанию при создании абстрактного класса в среде VisualStudio .NET в формируемый абстрактный класс автоматически вставляется только один метод - конструктор без параметров.
и тип ссылки совпадают CA
| using System; namespace MyDerriv1 { class Class1 { static void Main(string[] args) { // Тип объекта и тип ссылки совпадают CA var1; var1=new CA(); // Вызывается метод класса CA Console.WriteLine (var1.F1()); // Вызывается метод класса CA Console.WriteLine (var1.F2()); // Тип объекта - CB , а тип ссылки - CA CA var2; var2=new CB(); // Вызывается метод класса CA Console.WriteLine (var2.F1()); // Вызывается метод класса CB Console.WriteLine (var2.F2()); } } } // Класс CA using System; namespace MyDerriv1 { public class CA { public CA() { } public int F1() { return 1; } public virtual string F2() {return "Метод F2 класса CA";} } } // Класс CB using System; namespace MyDerriv1 { public class CB : MyDerriv1.CA { public CB() { } public int F1() {return 2; } // Переопределение виртуального метода F2 public override string F2() { return "Метод F2 класса CB"; } } } |
| Листинг 16.1. |
| Закрыть окно |
using System;
namespace MyDerriv1
{
class Class1
{
static void Main(string[] args)
{
// Тип объекта и тип ссылки совпадают
CA var1; var1=new CA();
// Вызывается метод класса CA
Console.WriteLine (var1.F1());
// Вызывается метод класса CA
Console.WriteLine (var1.F2());
// Тип объекта - CB , а тип ссылки - CA
CA var2; var2=new CB();
// Вызывается метод класса CA
Console.WriteLine (var2.F1());
// Вызывается метод класса CB
Console.WriteLine (var2.F2());
}
}
}
// Класс CA
using System;
namespace MyDerriv1
{
public class CA
{ public CA() { }
public int F1() { return 1; }
public virtual string F2()
{return "Метод F2 класса CA";}
}
}
// Класс CB
using System;
namespace MyDerriv1
{ public class CB : MyDerriv1.CA
{
public CB() { }
public int F1() {return 2; }
// Переопределение виртуального метода F2
public override string F2()
{ return "Метод F2 класса CB"; }
}
}
определяющий однопотоковую модель static void
| using System; namespace MyAClass1 { class Class1 {[STAThread] // Класс // System.STAThreadAttribute, // определяющий однопотоковую модель static void Main(string[] args) { CB varb1 = new CB(); /* Запрос о совместимости типа объекта с типом наследуемого им интерфейса */ if (varb1 is IA) /* Если переменная типа CB является переменной типа класса, реализующего запрашиваемый интерфейс IA, то оператор is вернет значение true. */ {Console.WriteLine ("varb1 - это ссылка на класс, который реализует интерфейс IA");} // Создание объекта типа интерфейса IA IA ivar=(IA)varb1; if (ivar is IA) {Console.WriteLine ("ivar - это ссылка на интерфейс IA");} bool var1=ivar.F1(); Console.WriteLine("Вызов метода F1 интерфейса IA: {0}",var1); // Приведение объекта к типу интерфейса Object ivar1 = varb1 as IA; if (ivar1 != null) Console.WriteLine ( "ivar1 - это ссылка на интерфейс IA"); } } } using System; namespace MyAClass1 { public class CB : MyAClass1.CA,IA {public CB() { } public bool F1() { return true; } public int F2(int a) { return a*10; } } interface IA { bool F1();} } using System; namespace MyAClass1 { public abstract class CA { public CA() { } public abstract int F2(int a); } } |
| Листинг 16.2. |
| Закрыть окно |
using System;
namespace MyAClass1
{ class Class1
{[STAThread] // Класс
// System.STAThreadAttribute,
// определяющий однопотоковую модель
static void Main(string[] args)
{
CB varb1 = new CB();
/* Запрос о совместимости типа объекта с типом наследуемого им интерфейса */
if (varb1 is IA)
/* Если переменная типа CB является переменной типа класса, реализующего запрашиваемый интерфейс IA, то оператор is вернет значение true. */
{Console.WriteLine ("varb1 - это ссылка на
класс, который реализует интерфейс IA");}
// Создание объекта типа интерфейса IA
IA ivar=(IA)varb1;
if (ivar is IA)
{Console.WriteLine ("ivar - это ссылка на интерфейс IA");}
bool var1=ivar.F1();
Console.WriteLine("Вызов метода F1 интерфейса IA: {0}",var1);
// Приведение объекта к типу интерфейса
Object ivar1 = varb1 as IA;
if (ivar1 != null)
Console.WriteLine ( "ivar1 - это ссылка на
интерфейс IA");
}
}
}
using System;
namespace MyAClass1
{
public class CB : MyAClass1.CA,IA
{public CB() { }
public bool F1() { return true; }
public int F2(int a) { return a*10; }
}
interface IA { bool F1();}
}
using System;
namespace MyAClass1
{ public abstract class CA
{ public CA() { }
public abstract int F2(int a);
}
}
Механизмы наследования
Объявление класса в языке C# создает новый ссылочный тип, определяющий как описание методов, так и их реализацию.Объявление интерфейса создает новый ссылочный тип, который специфицирует описание методов и имена некоторых констант, но не определяет саму реализацию.
Интерфейс может быть объявлен для расширения одного или нескольких интерфейсов.
Наследование позволяет определять новые классы в терминах существующих классов. В языке C# поддерживается только простое наследование: любой подкласс является производным только от одного непосредственного суперкласса. При этом любой класс может наследоваться от нескольких интерфейсов.
Методы - члены класса
В среде VisualStudio.NET добавить в класс новый метод можно, используя контекстное меню окна Class View. На рис. 16.2 приведен диалог C# MetodWizard, позволяющий определить модификаторы метода и список формальных параметров.
Рис. 16.2. Диалог C# MetodWizard
Язык C# поддерживает следующие модификаторы метода члена класса:
Порядок указания модификаторов доступа и модификаторов метода несущественен.
Виртуальные и абстрактные методы всегда должны указываться с модификатором доступа public.
Определение интерфейса
В языке C# отсутствует множественное наследование: каждый класс может иметь только один непосредственный базовый класс. Частичной заменой множественному наследованию может служить использование интерфейсов.Интерфейсы могут содержать свойства, методы и индексаторы, но без их реализации.
Один класс языка C# может наследовать несколько интерфейсов.
В C# интерфейс определяет новый ссылочный тип, содержащий объявления методов, которые обязательно должны быть реализованы в классе, наследующем данный интерфейс.
Можно сказать, что интерфейс определяет некоторую модель поведения, которая должна быть реализована в любом классе, наследующем данный интерфейс.
Объявление интерфейса может иметь следующее формальное описание:
[атрибуты] [модификаторы] interface имя_интерфейса [:список_базовых_интерфейсов] {тело_интерфейса}[;]
Например:
interface IMyInterface: IBase1, IBase2 { int M1(); int M2(); }
Если класс, наследующий интерфейс, не является абстрактным, то он обязательно должен реализовать методы, объявленные в интерфейсе. При наследовании интерфейса абстрактным классом методы, объявленные в интерфейсе, могут не иметь реализации только в том случае, если они объявляются с модификатором abstract.
Например:
public abstract class CMyInterface : IMyInterface { public CMyInterface() { // } public abstract int M1(); public abstract int M2(); } interface IMyInterface { int M1(); int M2(); }
Определение типа объекта
Для получения информации о том, является ли тип объекта времени выполнения совместимым с заданным типом, используется оператор is. Если типы совместимы, то оператор возвращает значение true.Оператор as аналогичен оператору is, с той лишь разницей, что выполняет приведение в случае совместимости типов или устанавливает ссылку на несовместимый объект равной null.
Применение оператора is имеет следующее формальное описание:
выражение is тип
Например:
string str1 = myObjects; if (str1 is string) Console.WriteLine ("тип string");
Применение оператора as имеет следующее формальное описание:
выражение as тип
Например:
string str1 = myObjects as string; if (str1 != null) Console.WriteLine ( "это строка" );
При этом предыдущая форма записи эквивалентна следующей записи:
выражение as тип ? (тип)выражение : (тип)null
Приведение типа объекта к типу интерфейса
При создании объект сразу можно приводить к типу интерфейса, используя оператор asНапример:
Object ivar1 = varb1 as IA;
Если приведение допустимо, то переменная будет содержать ссылку на интерфейс. Если приведение типа объекта типу интерфейса недопустимо, то в результате приведения типа переменная примет значение null.
Следующий пример иллюстрирует определение типов объектов и приведение типа объекта к типу интерфейса.
using System; namespace MyAClass1 { class Class1 {[STAThread] // Класс // System.STAThreadAttribute, // определяющий однопотоковую модель static void Main(string[] args) { CB varb1 = new CB(); /* Запрос о совместимости типа объекта с типом наследуемого им интерфейса */ if (varb1 is IA) /* Если переменная типа CB является переменной типа класса, реализующего запрашиваемый интерфейс IA, то оператор is вернет значение true. */ {Console.WriteLine ("varb1 - это ссылка на класс, который реализует интерфейс IA");} // Создание объекта типа интерфейса IA IA ivar=(IA)varb1; if (ivar is IA) {Console.WriteLine ("ivar - это ссылка на интерфейс IA");} bool var1=ivar.F1(); Console.WriteLine("Вызов метода F1 интерфейса IA: {0}",var1); // Приведение объекта к типу интерфейса Object ivar1 = varb1 as IA; if (ivar1 != null) Console.WriteLine ( "ivar1 - это ссылка на интерфейс IA"); } } } using System; namespace MyAClass1 { public class CB : MyAClass1.CA,IA {public CB() { } public bool F1() { return true; } public int F2(int a) { return a*10; } } interface IA { bool F1();} } using System; namespace MyAClass1 { public abstract class CA { public CA() { } public abstract int F2(int a); } }
Листинг 16.2.
Производные классы
В среде VisualStudio.NET новый производный класс можно создать, используя окно ClassView (выполнив в нем команду контекстного меню Add|Add Class). Рисунок 16.1 иллюстрирует страницы диалога C# Class Wizard, предлагаемого средой VisualStudio.NET для создания нового класса.
Рис. 16.1. Диалог C# Class Wizard
Имя создаваемого класса указывается в поле Class Name на странице Class Options диалога C# Class Wizard.
В поле Namespace указывается пространство имен, к которому будет принадлежать создаваемый класс. По умолчанию проект размещается в пространстве имен, одноименным с именем проекта.
В поле Access выбирается модификатор доступа для создаваемого класса.
Для класса в языке C# возможно использование двух модификаторов доступа:
Сборка - это физический файл, который состоит из нескольких PE-файлов (portable executable), генерируемых компилятором среды .NET. В сборку входит декларация (manifest), содержащая описание сборки для управляющей среды .NET.
Класс может имеет один из следующих модификаторов класса:
Очевидно, что изолированный класс не может быть одновременно и абстрактным классом.
Объявление класса может иметь следующее формальное описание:
МодификаторДоступа МодификаторКласса class ИмяКласса : ИмяНаследуемогоКласса {ТелоКласса }
Тело класса содержит описание переменных, методов и вложенных классов и заключается в фигурные скобки. В частном случае тело класса может не содержать ни одного объявления.
Например:
namespace CA1 { public abstract class Class2 : CA1.Class1 { public Class2() { // TODO: Add constructor logic here } } }
Виртуальные методы
Виртуальные методы объявляются в базовом классе с ключевым словом virtual, а в производном классе могут быть переопределены. Метод, который переопределяет виртуальный, указывается ключевым словом override. Прототипы виртуальных методов как в базовом, так и в производном классе должны быть одинаковы.Применение виртуальных методов позволяет реализовывать механизм позднего связывания.
На этапе компиляции строится только таблица виртуальных методов, а конкретный адрес проставляется уже на этапе выполнения.
При вызове метода - члена класса действуют следующие правила:
При позднем связывании определение вызываемого метода происходит на этапе выполнения (а не при компиляции) в зависимости от типа объекта, для которого вызывается виртуальный метод.
При раннем связывании определение вызываемого метода происходит на этапе компиляции.
Например:
using System; namespace MyDerriv1 { class Class1 { static void Main(string[] args) { // Тип объекта и тип ссылки совпадают CA var1; var1=new CA(); // Вызывается метод класса CA Console.WriteLine (var1.F1()); // Вызывается метод класса CA Console.WriteLine (var1.F2()); // Тип объекта - CB , а тип ссылки - CA CA var2; var2=new CB(); // Вызывается метод класса CA Console.WriteLine (var2.F1()); // Вызывается метод класса CB Console.WriteLine (var2.F2()); } } } // Класс CA using System; namespace MyDerriv1 { public class CA { public CA() { } public int F1() { return 1; } public virtual string F2() {return "Метод F2 класса CA";} } } // Класс CB using System; namespace MyDerriv1 { public class CB : MyDerriv1.CA { public CB() { } public int F1() {return 2; } // Переопределение виртуального метода F2 public override string F2() { return "Метод F2 класса CB"; } } }
Листинг 16.1.
Вложенные классы
Язык C# позволяет создавать вложенные классы. Вложенный класс объявляется как член другого класса.Права доступа для вложенного класса могут быть или меньшими, или такими же, как у содержащего его класса. Так, вложенный класс не может быть общедоступным, если содержащий его класс объявлен как internal.
При доступе к имени вложенного класса из членов внешнего класса квалификация именем внешнего класса не требуется.
Например:
using System; namespace MyNClass1 {class Class1 { static void Main(string[] args) {Class1.Class2 var1= new Class1.Class2(); // Эквивалентно // записи: Class2 var1= new Class2(); //Вызов метода вложенного класса Console.WriteLine( var1.F1()); } class Class2 { private int a=12345; public int F1() {return this.a;} } } }

Введение в программирование
Атрибуты
Язык С# позволяет создавать атрибуты для различных элементов языка, таких как типы, методы, поля и свойства классов. Данные, хранимые в атрибутах, можно запрашивать во время выполнения приложения. Атрибуты - это механизм, позволяющий создавать самоописывающиеся приложения.Использование атрибутов позволяет получать метаданные периода выполнения.Каждый атрибут - это экземпляр класса, производного от System.Attribute.
Назначаемый типу или члену класса атрибут указывается в квадратных скобках перед типом или членом класса.
Про атрибут, указанный для класса, иногда говорят, что этот атрибут "прикреплен к целевому типу".
Класс Attribute пространства имен System предоставляет следующие члены класса:
Для класса Attribute определено свойство TypeId, определяющее уникальный идентификатор атрибута.
Доступ к атрибуту
Значения атрибутов или их существование могут быть запрошены во время выполнения приложения.При запросе для класса или для члена класса данных о прикрепленных к нему атрибутах применяется отражение. Отражением называется функция, используемая во время выполнения приложения для получения метаданных, в том числе и заданных атрибутами.
Для реализации отражения библиотека NET Framework предоставляет несколько классов, базовым для которых служит класс отражения System.Reflection.
Основные методы отражения, используемые для запроса атрибутов, предоставляются классами System.Reflection.MemberInfo и System.Reflection.Assembly. Так, метод GetCustomAttributes позволяет определить атрибуты, присущие данному объекту.
Например:
class MyClass { public static void Main() { System.Reflection.MemberInfo info = typeof(MyClass); object[] attr = info.GetCustomAttributes(true); for (int i = 0; i < attr.Length; i ++) { System.Console.WriteLine(attr[i]); } } }
В результате выполнения данного кода в массив будут помещены классы всех назначенных атрибутов, а также класс System.Reflection.DefaultMemberAttribute. Доступ к типу объекта может быть реализован посредством класса Type . Метод Object.GetType возвращает объект типа Type , представляющий тип экземпляра объекта. Объект типа Type представляет собой ассоциированный с типом объект, используемый для доступа к метаданным типа.
Используя класс Type, можно создать объект для типа, экземпляр которого еще не был создан.
Например:
Type t= typeof(MyClass1);
Для создания объекта типа, ассоциированного с типом существующего экземпляра объекта, следует использовать метод GetType.
Например:
Type t = obj1.GetType(); , где obj1 экземпляр класса MyClass1.
При использовании объекта типа атрибута метод GetCustomAttribute возвращает значения атрибута, который назначен указанному параметрами объекту: первый параметр метода определяет ассоциированный объект, а второй - указывает класс атрибута.
Например:
MyAttribute MyAttr = (MyAttribute) Attribute.GetCustomAttribute( t, typeof(MyAttribute));
Следующий пример иллюстрирует доступ к значениям атрибутов.
class Class1 { static void Main(string[] args) { // Создание объекта, ассоциированного с типом AClass1 Type t= typeof(AClass1); MyAttribute MyAttr = (MyAttribute) Attribute.GetCustomAttribute( t, typeof (MyAttribute)); if(null == MyAttr) {Console.WriteLine("Атрибут не найден");} else { Console.WriteLine("Атрибут name = {0}, атрибут kod = {1}" , MyAttr.Name, // Возвращает значение // поля Name атрибута MyAttribute MyAttr.Kod); } } } [AttributeUsage(AttributeTargets.All)] public class MyAttribute : System.Attribute { private string name; private int kod; public MyAttribute(string name) {this.name = name; this.kod = 10; } public string Name { get { return name;} } public int Kod { get { return kod; } set {kod=value; } } }
[My("NameOfClass", Kod=123)] public class AClass1 { public AClass1() { } private int [] iArr = new int[10]; public int this[int ind1] { get { return iArr[ind1];} set { iArr[ind1]= value; } } }
Листинг 17.1.
с типом AClass1 Type t=
|
class Class1 { static void Main(string[] args) { // Создание объекта, ассоциированного с типом AClass1 Type t= typeof(AClass1); MyAttribute MyAttr = (MyAttribute) Attribute.GetCustomAttribute( t, typeof (MyAttribute)); if(null == MyAttr) {Console.WriteLine("Атрибут не найден");} else { Console.WriteLine("Атрибут name = {0}, атрибут kod = {1}" , MyAttr.Name, // Возвращает значение // поля Name атрибута MyAttribute MyAttr.Kod); } } } [AttributeUsage(AttributeTargets.All)] public class MyAttribute : System.Attribute { private string name; private int kod; public MyAttribute(string name) {this.name = name; this.kod = 10; } public string Name { get { return name;} } public int Kod { get { return kod; } set {kod=value; } } } [My("NameOfClass", Kod=123)] public class AClass1 { public AClass1() { } private int [] iArr = new int[10]; public int this[int ind1] { get { return iArr[ind1];} set { iArr[ind1]= value; } } } |
| Листинг 17.1. |
| Закрыть окно |
class Class1
{ static void Main(string[] args)
{
// Создание объекта, ассоциированного с типом AClass1
Type t= typeof(AClass1);
MyAttribute MyAttr =
(MyAttribute) Attribute.GetCustomAttribute(
t,
typeof (MyAttribute));
if(null == MyAttr)
{Console.WriteLine("Атрибут не найден");}
else
{
Console.WriteLine("Атрибут name = {0},
атрибут kod = {1}" ,
MyAttr.Name, // Возвращает значение
// поля Name атрибута MyAttribute
MyAttr.Kod); }
}
}
[AttributeUsage(AttributeTargets.All)]
public class MyAttribute : System.Attribute
{ private string name;
private int kod;
public MyAttribute(string name)
{this.name = name; this.kod = 10; }
public string Name { get { return name;} }
public int Kod { get { return kod; }
set {kod=value; } }
}
[My("NameOfClass", Kod=123)]
public class AClass1
{ public AClass1() { }
private int [] iArr = new int[10];
public int this[int ind1] {
get { return iArr[ind1];}
set { iArr[ind1]= value; } }
}
n Число методов public
|
public static void Main() { Type myType =(typeof(MyClass1)); // Получить методы с доступом public MethodInfo[] myArrMethodInfo = myType.GetMethods(BindingFlags.Public |BindingFlags.Instance |BindingFlags.DeclaredOnly); Console.WriteLine("\ n Число методов public =:" +myArrMethodInfo.Length); Console.WriteLine("Имена методов public : "); // Отобразить имена всех методов MyPrintMethodInfo(myArrMethodInfo); // Получить методы с защищенным доступом MethodInfo[] myArrMethodInfo1 = myType.GetMethods(BindingFlags.NonPublic |BindingFlags.Instance |BindingFlags.DeclaredOnly); Console.WriteLine("\n Число защищенных методов:" +myArrMethodInfo1.Length); } public static void MyPrintMethodInfo(MethodInfo[] a) { for(int i=0;i |
| Листинг 17.2. |
| Закрыть окно |
public static void Main()
{
Type myType =(typeof(MyClass1));
// Получить методы с доступом public
MethodInfo[] myArrMethodInfo =
myType.GetMethods(BindingFlags.Public
|BindingFlags.Instance
|BindingFlags.DeclaredOnly);
Console.WriteLine("\ n Число методов public =:"
+myArrMethodInfo.Length);
Console.WriteLine("Имена методов public : ");
// Отобразить имена всех методов
MyPrintMethodInfo(myArrMethodInfo);
// Получить методы с защищенным доступом
MethodInfo[] myArrMethodInfo1 =
myType.GetMethods(BindingFlags.NonPublic
|BindingFlags.Instance
|BindingFlags.DeclaredOnly);
Console.WriteLine("\n Число защищенных методов:"
+myArrMethodInfo1.Length);
}
public static void MyPrintMethodInfo(MethodInfo[] a)
{
for(int i=0;i
{
MethodInfo myMethodInfo = (MethodInfo)a[i];
Console.WriteLine("\n" + myMethodInfo.Name);
}
}
Индексаторы на базе многомерных массивов
Для одного класса может быть создано несколько индексаторов. Индексаторы должны различаться числом параметров.Например:
public class AClass1 { public AClass1() { } private int [] imyArray = new int[20]; public int this[int ind1] { get { return imyArray[ind1]; } set { imyArray[ind1]= value; } } private int [,] imyArray2 = new int[2,10]; public int this[int ind1, int ind2] { get {return imyArray2[ind1,ind2]; } set {imyArray2[ind1,ind2]= value; } } }
Используемость атрибута
Атрибуты могут быть использованы для различных элементов языка. Для того чтобы специфицировать, каким образом и для каких элементов можно использовать данный класс атрибута, библиотека NET Framework предоставляет класс System.AttributeUsageAttribute.Спецификация используемости атрибута указывается в квадратных скобках перед именем определением класса.
Например:
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
Элементы языка, которым может быть назначен атрибут, указываются значением или набором значений перечислимого типа AttributeTargets.
Например, для использования данного атрибута только для классов или методов перед определением класса атрибута следует записать:
[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)]
Спецификация используемости атрибута имеет следующее формальное описание:
[AttributeUsage( доступные_элементы, AllowMultiple=true_или_false, Inherited=наследуемость )]
Доступные элементы определяют те элементы языка, для которых может быть назначен данный атрибут. По умолчанию используется значение AttributeTargets.All (доступен для всех элементов).
Если именованный параметр AllowMultiple равен true, то классу или члену класса может быть назначено несколько атрибутов.
Параметр Inheritedопределяет, наследуется ли данный атрибут производным классом (по умолчанию - false).
Перечислимый тип AttributeTargets определяет следующее множество значений:
Элементы индексатора
Объект класса, используемого как аксессор, создается обычным образом. Инициализация элементов аксессора указывается как присвоение значений элементам массива. Доступ к элементам аксессора записывается как доступ к элементам массива.Например:
AClass1 ac1= new AClass1(); ac1[0]=5; ac1[1]=6; Console.WriteLine("ac1[0]= {0}, ac1[1]= {1}", ac1[0],ac1[1]);
Класс Type
Класс System.Type имеет следующее формальное описание:public abstract class Type : MemberInfo, IReflect.
Две ссылки типа Type на объект будут ссылаться на один и тот же объект только в том случае, если они представляют одинаковый тип.
Экземпляр типа Type может быть представлен любым из следующих типов:
Ссылка на объект Type, ассоциируемый с типом, может быть получена одним из следующих способов:
Метаданные - это информация о выполнимом модуле, получаемая во время выполнения приложения. К такой информации относятся и данные о типе. В случае неправильного указания имени типа возникает исключение. Поэтому указание типа следует заключать в блок try-catch .
Например:
try { Type tp=Type.GetType(s); Console.WriteLine("Имя типа : {0}",tp.FullName); } catch (System.NullReferenceException) {Console.WriteLine("Ошибка задания типа");}
Класс Type предоставляет большой набор свойств для запроса информации по типу, включая следующие:
Например:
Type myType = typeof(MyClass1); Guid myGuid =(Guid) myType.GUID;
Свойство IsByRef позволяет определить, передается ли указанный элемент по типу, а свойство Assembly определяет сборку.
Например:
Type tp=Type.GetType(s); Console.WriteLine("\tПередается по ссылке ? : {0}",tp.IsByRef);
Методы-аксессоры
После определения параметра в фигурных скобках указаны два метода-аксессора. get-аксессор возвращает значение данных по указанному индексу,а set-аксессор устанавливает значение данных с указанным индексом. Устанавливаемое значение задается ключевым словом value.Индексатор устанавливает и возвращает значения некоторого массива. Такой массив для аксессора должен быть создан. Типы используемого массива и аксессора должны совпадать. Например для целочисленного аксессора можно объявить следующий массив:
private int [] imyArray = new int[50];
Теперь, чтобы использовать акссессор, следует:
В результате класс, содержащий аксессор, будет иметь следующее объявление:
public class AClass1 { public AClass1() { } private int [] imyArray = new int[20];
public int this[int ind1] { get { return imyArray[ind1]; } set { imyArray[ind1]= value; } } }
Назначение атрибута
Для того, чтобы назначить атрибут элементу кода, можно:Атрибут указывается в квадратных скобках перед элементом, которому он назначается. Назначаемый атрибут инициализируется вызовом конструктора с соответствующими параметрами.
Класс System.ObsoleteAttribute позволяет помечать код и задавать информацию, которая будет отображаться как Warning во время компиляции приложения. Этот класс предназначается для возможности указания некоторого кода модуля как "устаревшего".Для того чтобы использовать существующий класс для назначаемого методу атрибута, следует:
Например:
[ObsoleteAttribute ("Сообщение, отображаемое компилятором как Warning")] public static void M1( ) {return ; // Компилятор будет выдавать предупреждение при // каждом вызове данного метода, которому назначен // соответствующий атрибут. Например: // c:\c#_project\pr1\class1.cs(23,4): warning // CS0618: 'pr1.Class1.M1()' is obsolete: // 'Сообщение, отображаемое компилятором как // Warning' }
Язык С# при назначении атрибута позволяет не указывать суффикс Attribute. Так, вместо [ObsoleteAttribute("")] можно записать [Obsolete("")].
Класс атрибута должен иметь хотя бы один public-конструктор.
Параметры атрибута
По умолчанию код класса атрибута содержит конструктор без параметров.Для того чтобы иметь возможность при назначении атрибута сохранять для типа или члена класса некоторые параметры, эти параметры надо задать как параметры конструктора класса атрибута. Параметры атрибута могут объявляться как переменные члены класса.Для доступа к защищенным переменным членам класса в языке C# используются свойства.
Для создания свойства в среде проектирования VisualStudio.NET можно использовать мастер построения свойства: для этого в окне Class View следует выделить секцию c именем класса и выполнить команду контекстного меню Add|Add Property.
В диалоге C# Property Wizard (рис. 17.2) в поле Property Name следует указать имя создаваемого свойства.

Рис. 17.2. Диалог C# Property Wizard
Тип создаваемого свойства выбирается из списка Property type.
На панели Accessors переключатели get и set определяют, какие методы-аксессоры будут созданы. Например:
using System; namespace MyPr1 { [AttributeUsage(AttributeTargets.All)] public class MyAttribute : System.Attribute { private string name; // Используются как private int kod; // параметры атрибута public MyAttribute(string name) { // Конструктор this.name = name; this.kod = 12345; } // Свойство Name public string Name { get { return name; } } // Свойство Kod public int Kod { get { return kod; } set {kod=value; } // Назначение // защищенной переменной члену // класса значения параметра } } } // Для назначения некоторому классу данного // атрибута перед объявлением класса следует // указать строку типа [My("NameClass", Kod=123)]
Получение информации о методах
Используя метод GetMetods, можно получить информацию о методах. Такая информация заносится в массив типа MetodInfo.Например:
public static void Main() { Type myType =(typeof(MyClass1)); // Получить методы с доступом public MethodInfo[] myArrMethodInfo = myType.GetMethods(BindingFlags.Public |BindingFlags.Instance |BindingFlags.DeclaredOnly); Console.WriteLine("\n Число методов public =:" +myArrMethodInfo.Length); Console.WriteLine("Имена методов public : "); // Отобразить имена всех методов MyPrintMethodInfo(myArrMethodInfo); // Получить методы с защищенным доступом MethodInfo[] myArrMethodInfo1 = myType.GetMethods(BindingFlags.NonPublic |BindingFlags.Instance |BindingFlags.DeclaredOnly); Console.WriteLine("\n Число защищенных методов:" +myArrMethodInfo1.Length); } public static void MyPrintMethodInfo(MethodInfo[] a) { for(int i=0;i

Позиционные и именованные параметры атрибута
При назначении классу или члену класса атрибута используется конструктор атрибута со списком параметров. Параметры могут быть:Позиционные параметры указываются в порядке, который определяется списком параметров конструктора атрибута. Позиционные параметры всегда должны быть указаны при назначении атрибута.
Именованные параметры отсутствуют в списке параметров конструктора атрибута. Значения, задаваемые для именованных параметров, используются для инициализации полей и свойств создаваемого экземпляра атрибута. Список именованных параметров указывается через запятую после списка позиционных параметров. Каждый именованный параметр определяется как Имя_параметра=Значение_параметра.
В предыдущем примере параметр name использовался как позиционный параметр, а kod - как именованный (по умолчанию значение переменной kod, доступной через свойство Kod, устанавливается равным конкретному значению. Если при назначении атрибута явно не будет задан именованный параметр, то при создании экземпляра атрибута будет использовано значение по умолчанию).
Параметры атрибута могут указываться константными значениями следующих типов:
Создание атрибута
Создание атрибута начинается с создания класса, реализующего атрибут. Такой класс должен быть наследуем от любого класса атрибута. Класс атрибута всегда должен иметь модификатор доступа public.Класс атрибута всегда непосредственно или опосредованно наследуется от класса System.Attribute.
Например:
public class MyAttribute : System.Attribute { public MyAttribute() { } }
Создание индексаторов
Индексатор позволяет работать с классом или структурой таким образом, как если бы это были массивы. Индексация класса выполняется по индексу, указываемому как параметр. Иногда классы, используемые как индексаторы, называют классами-индексаторами.Объявление индексатора может иметь следующее формальное описание:
атрибуты] [модификаторы] тип this [[атрибуты] тип_параметра идентификатор_параметра .,...] {объявление аксессоров}
Индексатор должен иметь как минимум один параметр. Тип и идентификатор параметра указываются в квадратных скобках после ключевого слова this.
Среда проектирования Visual Studio.NET позволяет использовать мастер создания индексатора: для этого в окне Class View следует выделить имя класса и выполнить команду контекстного меню Add|Add Indexer.
Диалог C# Indexer Wizard (рис. 17.1) позволяет определить параметры создаваемого индексатора.

Рис. 17.1. Диалог C# Indexer Wizard
В поле Indexer Аcess указывается модификатор доступа для индексатора. Это могут быть следующие модификаторы:
В поле Indexer Type определяется тип массива, используемого для индексатора. В качестве типа индексатора может быть выбран любой из следующих типов:
Поле Parameter Name содержит имя параметра.
На панели Indexer Modifiers можно выбрать одну из следующих опций:
Ключевое слово this используется как имя индексатора, так как с классом, содержащим индексатор, можно манипулировать как с массивом.
Например:
public int this[int ind1] // Индексатор типа int // с одним параметром типа int - ind1 { get { return 0; } set { } }
Введение в программирование
DLL-библиотеки
Для того чтобы вызвать метод из DLL-библиотеки, его следует объявить с модификатором extern и атрибутом DllImport.Например:
[DllImport("Имя_библиотеки.dll")] static extern int M1(int i1, string s1);
Класс атрибута DllImportAttribute имеет следующее определение:
namespace System.Runtime.InteropServices { [AttributeUsage(AttributeTargets.Method)] public class DllImportAttribute: System.Attribute { public DllImportAttribute(string dllName) {...} public CallingConvention CallingConvention; public CharSet CharSet; // Набор символов public string EntryPoint; // Имя метода public bool ExactSpelling; // Точное // соответствие написания имени метода public bool PreserveSig; // Определяет, будет ли // предотвращено изменение сигнатуры метода (по умолчанию // установлено значение true). // При изменении сигнатуры возвращаемое значение будет // иметь тип HRESULT и будет добавлен out-параметр retval public bool SetLastError; public string Value { get {...} } } }
Атрибут DllImport имеет один позиционный параметр, определяющий имя DLL-библиотеки, и пять именованных параметров. Параметр EntryPoint позволяет указать имя метода из DLL-библиотеки. При этом имя метода, помечаемого данным атрибутом, может отличаться от имени метода в DLL-библиотеке.
Например:
[DllImport("myDll.dll", EntryPoint="M1")] static extern int New_name_of_M1(int i1, string s1);
Именованный параметр CharSet определяет используемый в DLL-библиотеке набор символов (ANSI или Unicode). По умолчанию используется значение CharSet.Auto.
Например:
[DllImport("myDll.dll", CharSet CharSet.Ansi)] static extern int M1(int i1, string s1);
Для каждого типа параметра при вызове метода из DLL-библиотеки выполняющая среда .NET производит подразумеваемое по умолчанию преобразование типов (например, тип string в тип LPSTR (Win32). Для того чтобы явным образом указать тип, используемый в методе DLL-библиотеки, следует применить к параметру атрибут MarshalAsAttribute.
Например:
[DllImport("myDll.dll", CharSet CharSet.Unicode)] static extern int M1(int i1, [MarshalAs(UnmanagedType.LPWStr)] string s1);
Атрибут MarshalAsAttribute может быть прикреплен к полю, методу или параметру. Прикрепление данного атрибута к методу позволяет указать явное преобразование типа для возвращаемого значения.

Параметр callback используется для вызова
|
using System; namespace MyDelegatе1 { class Class1 {[STAThread] static void Main(string[] args) { CA var1= new CA(); // Создание экземпляра делегата CA.Metod1Callback myCall = new CA.Metod1Callback(Class1.Metod2); // Значение // параметра, передаваемое методу обратного // вызова Class1.Metod2, определено // в методе Metod1 как "1". // Выполнение метода обратного вызова (Metod2), // переданного в качестве параметра CA.Metod1(myCall); } static void Metod2 (string str2){ Console.WriteLine("Выполняется метод Metod2"); Console.WriteLine(str2); } } } using System; namespace MyDelegatе1 { public class CA { public CA() { } public delegate void Metod1Callback(string str1); public static void Metod1(Metod1Callback callback) { Console.WriteLine("Выполняется метод Metod1"); // Параметр callback используется для вызова // метода обратного вызова и передачи ему // параметра (строки "1") callback("1"); } } } |
| Листинг 18.1. |
| Закрыть окно |
using System;
namespace MyDelegatе1
{
class Class1
{[STAThread]
static void Main(string[] args) {
CA var1= new CA();
// Создание экземпляра делегата
CA.Metod1Callback myCall =
new CA.Metod1Callback(Class1.Metod2); // Значение
// параметра, передаваемое методу обратного
// вызова Class1.Metod2, определено
// в методе Metod1 как "1".
// Выполнение метода обратного вызова (Metod2),
// переданного в качестве параметра
CA.Metod1(myCall);
}
static void Metod2 (string str2){
Console.WriteLine("Выполняется метод Metod2");
Console.WriteLine(str2);
}
}
}
using System;
namespace MyDelegatе1
{
public class CA
{ public CA() { }
public delegate void Metod1Callback(string str1);
public static void Metod1(Metod1Callback callback)
{
Console.WriteLine("Выполняется метод Metod1");
// Параметр callback используется для вызова
// метода обратного вызова и передачи ему
// параметра (строки "1")
callback("1");
}
}
}
Использование делегата для вызова методов
Делегат объявляет новый ссылочный тип.Делегат позволяет передавать функцию как параметр.
Объявление делегата имеет следующее формальное описание:
[атрибуты] [модификаторы] delegate тип_результата имя_делегата ([список_формальных параметров]);
Модификаторами делегата могут быть:
Тип результата должен соответствовать типу результата используемого метода. При создании делегата требуется, чтобы передаваемый как делегат метод имел ту же сигнатуру метода, что и в объявлении делегата.
Например:
using System; delegate void MyDelegate(); // Этот делегат позволяет // вызывать методы без параметров // и без возвращаемого значения
Для вызова метода через делегата следует создать экземпляр делегата, передав ему в качестве параметра метод, имеющий ту же сигнатуру, что и у делегата, а затем выполнить вызов. Для статического метода в качестве параметра передается имя метода, квалифицированное именем класса.
Например:
using System; delegate void MyDelegate(); namespace MyDelegat1 { class Class1 {[STAThread] static void Main(string[] args) { CA var1= new CA(); // Экземпляр делегата для нестатического метода: MyDelegate F_d = new MyDelegate(var1.F_Instance); F_d(); // Экземпляр делегата для статического метода: F_d = new MyDelegate(CA.F_Static); F_d(); } } public class CA { public CA() { } public void F_Instance() { Console.WriteLine("Вызов метода класса с использованием делегата"); } public static void F_Static() { Console.WriteLine("Вызов статического метода с использованием делегата"); } }
Небезопасный код
Фрагмент небезопасного кода следует помечать ключевым словом unsafe.Например:
int i1; unsafe {int *i2=&i1;}
Ключевым словом unsafe требуется обязательно помечать любой фрагмент кода, который содержит указатель.
Модификатор unsafe может быть указан для методов, свойств и конструкторов (за исключением статических конструкторов), а также для блоков кода.
Например:
using System; class Class1 { unsafe static void M1 (int* p) // Небезопасный код: указатель на int { *p *= *p; } // *p - доступ к значению unsafe public static void Main() // Небезопасный код: применен оператор // получения адреса (&) { int i = 4; M1 (&i); Console.WriteLine (i); } }
Чтобы использовать небезопасный код, следует установить опцию компилятора /unsafe. Для этого достаточно выбрать имя проекта в окне Solution Explorer и через контекстное меню вызвать диалогProperty Pages (рис. 18.1) , а затем на странице Configuration Properties | Build установить значение опции Allow unsafe code blocks равным True.

Рис. 18.1. Диалог Property Pages
Указатели можно использовать только с размерными типами, массивами и строками. При задании указателя на массив первый элемент массива должен быть размерного типа.
Выполняющая среда .NET для эффективного управления памятью при удалении одних объектов "перемещает" другие объекты, чтобы исключить фрагментацию памяти свободными блоками памяти. Таким образом, выполняющая среда .NET по умолчанию не гарантирует, что объект, на который получен указатель, всегда будет иметь один и тот же адрес. Для предотвращения перемещения объекта следует использовать оператор fixed, который имеет следующее формальное описание:
fixed ( тип* имя_указателя = выражение ) выполняемый_оператор_или_блок
В качестве типа можно указывать любой неуправляемый тип или void. Выражение является указателем на заданный тип. Фиксация объекта применяется только для указанного выполняемого оператора или блока. Доступ к фиксированной переменной не ограничен областью видимости небезопасного кода.
Поэтому фиксированная переменная может указывать на значение, располагаемое в более широкой области видимости, чем данная область видимости небезопасного кода. При этом компилятор C# не выдает предупреждений о такой ситуации.
Однако компилятор C# не позволит установить указатель на управляемую переменную вне оператора fixed.
Например:
class Point { public int x, y; } class Class1 {[STAThread] static void Main(string[] args) { unsafe { Point pt = new Point(); // pt - это управляемая // переменная fixed ( int* p = &pt.x ) { *p = 1 } // pt.x - // указывает на значение // размерного типа } } }
Одним оператором fixed можно фиксировать несколько указателей, но только в том случае, если они одного типа.
Например:
fixed (byte* pa1 = array1, pa2 = array2) {...}
В том случае, если требуется зафиксировать указатели различных типов, можно использовать вложенные операторы fixed.
Например:
fixed (int* p1 = &p.x) fixed (double* p2 = &array1[5]) { }
Указатели, которые инициализированы оператором fixed, не могут быть изменены. Если объект, на который устанавливается указатель, размещается в стеке (например, переменная типа int), то его местоположение фиксировать не требуется.
Разместить блок памяти в стеке можно и явным образом, используя оператор stackalloc, который имеет следующее формальное описание:
тип * имя_указателя = stackalloc тип [ выражение ];
В качестве типа может быть указан любой неуправляемый тип.
Например:
using System; class Class1 {public static unsafe void Main() { int* a1 = stackalloc int[100]; int* p = a1; // Указатель на первый // элемент массива *p++ = *p++ = 1; for (int i=2; i<100; ++i, ++p) *p = p[-1] + p[-2]; for (int i=0; i<10; ++i) Console.WriteLine (a1[i]); } }
Время жизни указателя, инициализированного с применением stackalloc, ограничено временем выполнения метода, в котором этот указатель объявлен. Инициализировать указатели посредством stackalloc можно только для локальных переменных.
Применение делегатов как методов обратного вызова
Метод обратного вызова используется для передачи одному методу в качестве параметра другого метода, который может быть вызван через переданный "указатель" на метод.Методы обратного вызова могут применяться в различных целях. Наиболее часто их используют для реализации асинхронной обработки данных или определения кода, выполняющего дополнительную обработку данных.
Например:
using System; namespace MyDelegatе1 { class Class1 {[STAThread] static void Main(string[] args) { CA var1= new CA(); // Создание экземпляра делегата CA.Metod1Callback myCall = new CA.Metod1Callback(Class1.Metod2); // Значение // параметра, передаваемое методу обратного // вызова Class1.Metod2, определено // в методе Metod1 как "1".
// Выполнение метода обратного вызова (Metod2), // переданного в качестве параметра CA.Metod1(myCall); } static void Metod2 (string str2){ Console.WriteLine("Выполняется метод Metod2"); Console.WriteLine(str2); } } } using System; namespace MyDelegatе1 { public class CA { public CA() { } public delegate void Metod1Callback(string str1); public static void Metod1(Metod1Callback callback) { Console.WriteLine("Выполняется метод Metod1"); // Параметр callback используется для вызова // метода обратного вызова и передачи ему // параметра (строки "1") callback("1"); } } }
Листинг 18.1.
В результате выполнения этого приложения сначала будет вызван метод Metod1, а затем Metod2.
Применение неуправляемого кода
По умолчанию приложения на C# относятся к управляемому коду. Но при необходимости управляемый код может взаимодействовать с неуправляемым кодом. К неуправляемому коду, вызываемому из управляемых C# приложений, можно отнести функции DLL-библиотек и сервисы COM-компонентов. Приложение управляемого кода на языке C# также может включать фрагменты небезопасного кода. Небезопасный код тоже относится к неуправляемому коду, так как выделение и освобождение памяти в нем не контролируется исполняющей средой .NET.Введение в программирование
Класс диалога имеет следующее объявление
|
// Заголовочный файл class CD1App : public CWinApp {public: CD1App(); public: virtual BOOL InitInstance(); // Первый // выполняемый метод // Implementation DECLARE_MESSAGE_MAP() }; extern CD1App theApp; // Переменная - приложение // Файл реализации D1.cpp #include "stdafx.h" #include "D1.h" #include "D1Dlg.h" // CD1App BEGIN_MESSAGE_MAP(CD1App, CWinApp) // Обрабатываемые события ON_COMMAND(ID_HELP, &CWinApp::OnHelp) END_MESSAGE_MAP() CD1App::CD1App(){ } // Конструктор CD1App theApp; BOOL CD1App::InitInstance() { CWinApp::InitInstance(); AfxEnableControlContainer(); SetRegistryKey(_T("Local AppWizard-Generated Applications")); CD1Dlg dlg; // Создание объекта диалога m_pMainWnd = &dlg; INT_PTR nResponse = dlg.DoModal(); // Отображение // диалога if (nResponse == IDOK) { } else if (nResponse == IDCANCEL) { } return FALSE; // Завершение приложения } Класс диалога имеет следующее объявление и реализацию: // Заголовочный файл class CD1Dlg : public CDialog {public: CD1Dlg(CWnd* pParent = NULL); // Конструктор enum { IDD = IDD_D1_DIALOG }; // Ресурс диалога protected: // Поддержка DDX/DDV: virtual void DoDataExchange(CDataExchange* pDX); // Реализация protected: HICON m_hIcon; // Функции таблицы обрабатываемых событий virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() }; // Файл реализации D1Dlg.cpp #include "stdafx.h" #include "D1.h" #include "D1Dlg.h" class CAboutDlg : public CDialog // Класс // вспомогательного диалога {public: CAboutDlg(); enum { IDD = IDD_ABOUTBOX }; // Ресурс диалога protected: virtual void DoDataExchange(CDataExchange* pDX); protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX);} BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) END_MESSAGE_MAP() // Класс окна диалога приложения CD1Dlg::CD1Dlg(CWnd* pParent /*=NULL*/) : CDialog(CD1Dlg::IDD, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CD1Dlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CD1Dlg, CDialog) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() END_MESSAGE_MAP() // Методы обработки событий BOOL CD1Dlg::OnInitDialog() { CDialog::OnInitDialog(); ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); // Указатель // на системное меню if (pSysMenu != NULL) // Добавление пунктов к { CString strAboutMenu; //системному меню strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } SetIcon(m_hIcon, TRUE); // Определение крупной // пиктограммы SetIcon(m_hIcon, FALSE); // Определение мелкой пиктограммы return TRUE; } void CD1Dlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialog::OnSysCommand(nID, lParam); } } void CD1Dlg::OnPaint() { CDialog::OnPaint(); } HCURSOR CD1Dlg::OnQueryDragIcon() // Запрос курсора { return static_cast |
| Листинг 19.1. |
| Закрыть окно |
END_MESSAGE_MAP()
// Класс окна диалога приложения
CD1Dlg::CD1Dlg(CWnd* pParent /*=NULL*/)
: CDialog(CD1Dlg::IDD, pParent)
{ m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); }
void CD1Dlg::DoDataExchange(CDataExchange* pDX)
{ CDialog::DoDataExchange(pDX); }
BEGIN_MESSAGE_MAP(CD1Dlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
END_MESSAGE_MAP()
// Методы обработки событий
BOOL CD1Dlg::OnInitDialog()
{ CDialog::OnInitDialog();
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE); // Указатель
// на системное меню
if (pSysMenu != NULL) // Добавление пунктов к
{ CString strAboutMenu; //системному меню
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{ pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING,
IDM_ABOUTBOX, strAboutMenu);
}
}
SetIcon(m_hIcon, TRUE); // Определение крупной
// пиктограммы
SetIcon(m_hIcon, FALSE); // Определение мелкой пиктограммы
return TRUE;
}
void CD1Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{ if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{ CAboutDlg dlgAbout; dlgAbout.DoModal(); }
else { CDialog::OnSysCommand(nID, lParam); }
}
void CD1Dlg::OnPaint() { CDialog::OnPaint(); }
HCURSOR CD1Dlg::OnQueryDragIcon() // Запрос курсора
{ return static_cast(m_hIcon); }
данного диалога или NULL HINSTANCE
|
typedef struct tagOFN { DWORD lStructSize; // Длина структуры в байтах HWND hwndOwner; // Дескриптор окна владельца // данного диалога или NULL HINSTANCE hInstance; LPCTSTR lpstrFilter; // Указатель на буфер, содержащий // пары null-ограниченных строк: // первая строка - это описание // фильтра (например: "Программы"), // вторая строка - шаблон // (например: "*.exe;*.com")" LPTSTR lpstrCustomFilter; DWORD nMaxCustFilter; DWORD nFilterIndex; LPTSTR lpstrFile; // Указатель на буфер, // который содержит // имя файла, используемое для // инициализации поля File Name, // или NULL. При успешном завершении // функции GetOpenFileName или //GetSaveFileName поле содержит // логический диск, путь и имя // выбранного файла. DWORD nMaxFile; LPTSTR lpstrFileTitle; // Указатель на буфер, // содержащий имя и расширение выбранного файла DWORD nMaxFileTitle; LPCTSTR lpstrInitialDir; // Указатель на null- // ограниченную строку, которая представляет каталог, // используемый для инициализации диалога LPCTSTR lpstrTitle; DWORD Flags; WORD nFileOffset; WORD nFileExtension; LPCTSTR lpstrDefExt; LPARAM lCustData; LPOFNHOOKPROC lpfnHook; LPCTSTR lpTemplateName; #if (_WIN32_WINNT >= 0x0500) void * pvReserved; DWORD dwReserved; DWORD FlagsEx; #endif // (_WIN32_WINNT >= 0x0500) } OPENFILENAME, *LPOPENFILENAME; |
| Листинг 19.2. |
| Закрыть окно |
typedef struct tagOFN {
DWORD lStructSize; // Длина структуры в байтах
HWND hwndOwner; // Дескриптор окна владельца
// данного диалога или NULL
HINSTANCE hInstance;
LPCTSTR lpstrFilter; // Указатель на буфер, содержащий
// пары null-ограниченных строк:
// первая строка - это описание
// фильтра (например: "Программы"),
// вторая строка - шаблон
// (например: "*.exe;*.com")"
LPTSTR lpstrCustomFilter;
DWORD nMaxCustFilter;
DWORD nFilterIndex;
LPTSTR lpstrFile; // Указатель на буфер,
// который содержит
// имя файла, используемое для
// инициализации поля File Name,
// или NULL. При успешном завершении
// функции GetOpenFileName или
//GetSaveFileName поле содержит
// логический диск, путь и имя
// выбранного файла.
DWORD nMaxFile;
LPTSTR lpstrFileTitle; // Указатель на буфер,
// содержащий имя и расширение выбранного файла
DWORD nMaxFileTitle;
LPCTSTR lpstrInitialDir; // Указатель на null-
// ограниченную строку, которая представляет каталог,
// используемый для инициализации диалога
LPCTSTR lpstrTitle;
DWORD Flags;
WORD nFileOffset;
WORD nFileExtension;
LPCTSTR lpstrDefExt;
LPARAM lCustData;
LPOFNHOOKPROC lpfnHook;
LPCTSTR lpTemplateName;
#if (_WIN32_WINNT >= 0x0500)
void * pvReserved;
DWORD dwReserved;
DWORD FlagsEx;
#endif // (_WIN32_WINNT >= 0x0500)
} OPENFILENAME, *LPOPENFILENAME;
Класс CColorDialog
Класс CColorDialog предназначается для реализации стандартного диалога Color, используемого для выбора цвета. Структура CHOOSECOLOR применяется для инициализации диалога. Для отображения стандартного диалога Color вызывается метод ChooseColor.Например:
CHOOSECOLOR cc; // Структура для инициализации диалога static COLORREF acrCustClr[16]; // Массив цветов HWND hwnd; // Окно владелец HBRUSH hbrush; // Указатель кисти static DWORD rgbCurrent; // Первоначально выбранный цвет ZeroMemory(&cc, sizeof(cc)); cc.lStructSize = sizeof(cc); cc.hwndOwner = hwnd; cc.lpCustColors = (LPDWORD) acrCustClr; cc.rgbResult = rgbCurrent; cc.Flags = CC_FULLOPEN | CC_RGBINIT;
if (ChooseColor(&cc)==TRUE) { hbrush = CreateSolidBrush(cc.rgbResult); rgbCurrent = cc.rgbResult; }

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

Для создания диалоговых окон на основе существующего ресурса диалога можно использовать:
Если шаблон диалога отсутствует, то можно создать шаблон диалога, используя структуру DLGTEMPLATE, а затем, после создания объекта диалога, вызвать методы CreateIndirect или InitModalIndirect и DoModal.
Модальное диалоговое окно автоматически закрывается при щелчке пользователя на кнопках типа OK или Cancel или при вызове метода EndDialog. Отметим, что метод EndDialog только скрывает окно диалога, а для его удаления следует использовать функцию DestroyWindow.
Для обмена значениями с элементом управления используется метод CWnd::UpdateData. Этот метод автоматически вызывается и при щелчке пользователя на кнопке типа OK, и при инициализации элементов управления методом OnInitDialog.
Метод OnInitDialog вызывается после создания всех элементов управления диалогового окна, но перед их отображением.
Для определения фона диалоговых окон используется метод CWinApp::SetDialogBkColor.
Последовательность создания диалогового окна включает следующие шаги:
| вызов конструктора с параметром, указывающим ID шаблона диалога | вызов конструктора без параметра шаблона диалога |
| вызов метода DoModal | вызов метода Create с параметром, указывающим ID шаблона диалога |
Класс CDialog предоставляет следующие конструкторы объекта:
CDialog (LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL); CDialog (UINT nIDTemplate, CWnd* pParentWnd = NULL); CDialog ();
Параметр lpszTemplateName - это строка, содержащая имя ресурса шаблона диалога, nIDTemplate - значение ID ресурса шаблона диалога. По умолчанию редактор ресурсов присваивает шаблонам диалога идентификаторы, начинающиеся с префикса IDD_. Параметр pParentWnd задает указатель на родительское окно или окно собственник (типа CWnd), которому принадлежит этот диалог. Значение NULL показывает, что родительским окном является главное окно приложения.
Конструктор без параметров используется для создания модального окна диалога на основе шаблона, расположенного в памяти. При этом применяется метод InitModalIndirect. Для создания модального диалога после вызова конструктора следует вызвать метод DoModal.
Класс CDialog предоставляет следующие методы Create для создания немодального диалога:
BOOL Create (LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL); BOOL Create (UINT nIDTemplate, CWnd* pParentWnd = NULL);
При успешном завершении создания диалогового окна и инициализации эти методы возвращают ненулевое значение.
Если в шаблоне диалога не установлен стиль WS_VISIBLE, то для отображения диалога необходимо вызвать метод ShowWindow.
Класс CDialog предоставляет большой набор методов, предназначенных для работы с диалоговым окном, включая следующие:
Класс CFileDialog
Класс CFileDialog инкапсулирует поведение диалогов Open и Save As. Для работы с объектом типа CFileDialog сначала следует создать этот объект, используя конструктор, а затем управлять данным компонентом через поля структуры m_ofn типа OPENFILENAME.Структура OPENFILENAME содержит информацию, используемую функциями GetOpenFileName и GetSaveFileName для инициализации диалогов Open или Save As .
Структура OPENFILENAME имеет следующее определение:
typedef struct tagOFN { DWORD lStructSize; // Длина структуры в байтах HWND hwndOwner; // Дескриптор окна владельца // данного диалога или NULL HINSTANCE hInstance; LPCTSTR lpstrFilter; // Указатель на буфер, содержащий // пары null-ограниченных строк: // первая строка - это описание // фильтра (например: "Программы"), // вторая строка - шаблон // (например: "*.exe;*.com")" LPTSTR lpstrCustomFilter; DWORD nMaxCustFilter; DWORD nFilterIndex; LPTSTR lpstrFile; // Указатель на буфер, // который содержит // имя файла, используемое для // инициализации поля File Name, // или NULL. При успешном завершении // функции GetOpenFileName или //GetSaveFileName поле содержит // логический диск, путь и имя // выбранного файла. DWORD nMaxFile; LPTSTR lpstrFileTitle; // Указатель на буфер, // содержащий имя и расширение выбранного файла DWORD nMaxFileTitle; LPCTSTR lpstrInitialDir; // Указатель на null- // ограниченную строку, которая представляет каталог, // используемый для инициализации диалога LPCTSTR lpstrTitle; DWORD Flags; WORD nFileOffset; WORD nFileExtension; LPCTSTR lpstrDefExt; LPARAM lCustData; LPOFNHOOKPROC lpfnHook; LPCTSTR lpTemplateName; #if (_WIN32_WINNT >= 0x0500) void * pvReserved; DWORD dwReserved; DWORD FlagsEx; #endif // (_WIN32_WINNT >= 0x0500) } OPENFILENAME, *LPOPENFILENAME;
Листинг 19.2.
Если метод DoModal, используемый для отображения диалога типа CFileDialog, возвращает значение IDOK, то для получения информации об имени файла можно использовать методы члены класса CFileDialog.
Если предполагается разрешить пользователю одновременно выбрать более одного файла, то до вызова метода DoModal следует установить флаг OFN_ALLOWMULTISELECT.
Например:
OPENFILENAME ofn; // Данные для диалога char szFile[260]; // Буфер для имени файла HWND hwnd; // Окно - владелец отображаемого диалога HANDLE hf; // Дексриптор файла // Инициализация структуры OPENFILENAME ZeroMemory(&ofn, sizeof(OPENFILENAME)); // Макрос, // заполняющий нулями указанный блок памяти ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = hwnd; ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile); ofn.lpstrFilter = "Все\0*.*\0Текстовые\0*.TXT\0"; ofn.nFilterIndex = 1; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = NULL; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; // Отображение диалога Open if (GetOpenFileName(&ofn)==TRUE) hf = CreateFile(ofn.lpstrFile, GENERIC_READ, 0, (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);
Класс CFileDialog содержит поле m_ofn, которое является структурой типа OPENFILENAME, используемой для инициализации диалогов Open и Save As.
Для создания данного диалога применяется следующий конструктор:
CFileDialog( BOOL bOpenFileDialog, LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL, DWORD dwSize = sizeof( OPENFILENAME ));
Параметр bOpenFileDialogопределяет типа создаваемого диалога: значение TRUE указывает диалог Open, а FALSE - диалог Save As. Параметр lpszDefExt указывает используемое по умолчанию расширение файла. Если пользователь не вводит расширение файла в поле File name, то данное значение автоматически добавляется к имени файла. Если значение этого параметра равно NULL, то расширение не добавляется. Параметр lpszFileName - это NULL или имя файла, отображаемое при открытии данного диалога. Параметр dwFlags определяет флажки, используемые для настройки диалога; lpszFilter - это пары строк, определяющие фильтры для выбора файлов. При использовании структуры типа OPENFILENAME строки фильтра следует ограничивать символом '\0', а в данном параметре следует использовать символ |.
Параметр pParentWnd- это указатель на окно владелец или родительское окно данного диалога. Размер структуры OPENFILENAME указывается параметром dwSize.
Например:
void CMyDlg::OnFileOpen() { // szFilters - строка с описанием и шаблоном фильтров char CMyDlg::szFilters[]= "Текстовые файлы (*.txt)|*.my|Все файлы(*.*)|*.*||"; // Создание диалога Open, использующего // для отображаемых файлов расширение txt CFileDialog fileDlg = new CFileDialog (TRUE, "txt", "*.txt", OFN_FILEMUSTEXIST| OFN_HIDEREADONLY, szFilters, this); if( fileDlg.DoModal ()==IDOK ) // Отображение диалога { CString pathName = fileDlg.GetPathName(); CString fileName = fileDlg.GetFileTitle (); } }
Отображение диалога стандартного Open также можно выполнить вызовом метода GetOpenFileName.
Например:
if (GetOpenFileName(&ofn)==TRUE) hf = CreateFile(ofn.lpstrFile, GENERIC_READ, 0, (LPSECURITY_ATTRIBUTES) NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);
Классы диалогов библиотеки MFC
Библиотека MFC содержит большой набор классов, предназначенных для создания диалоговых окон. На следующей схеме приведена иерархия классов диалогов, наследуемых от класса CDialog.MFC-приложения
MFC-приложения можно реализовывать как:Тип создаваемого приложения выбирается на странице Application type мастера создания MFC-приложения (рис. 19.1).

Рис. 19.1. Страница Application Тype мастера создания MFC-приложения
Черты пользовательского интерфейса для создаваемого диалога выбираются на страницах User Interface Features (рис.19.2) и Advanced Features (рис. 19.3) мастера создания MFC-приложения.

Рис. 19.2. Страница User Interface Features мастера создания MFC-приложения

Рис. 19.3. Страница Advanced Features мастера создания MFC-приложения
Приложения-диалоги
Базовым классом любого Windows-приложения, основанного на оконном интерфейсе, является класс cWinApp.При создании приложения-диалога мастер построения MFC-приложения добавляет в проект два класса:
Класс приложения имеет следующее объявление и реализацию:
// Заголовочный файл class CD1App : public CWinApp {public: CD1App(); public: virtual BOOL InitInstance(); // Первый // выполняемый метод // Implementation DECLARE_MESSAGE_MAP() }; extern CD1App theApp; // Переменная - приложение // Файл реализации D1.cpp #include "stdafx.h" #include "D1.h" #include "D1Dlg.h" // CD1App BEGIN_MESSAGE_MAP(CD1App, CWinApp) // Обрабатываемые события ON_COMMAND(ID_HELP, &CWinApp::OnHelp) END_MESSAGE_MAP() CD1App::CD1App(){ } // Конструктор CD1App theApp; BOOL CD1App::InitInstance() { CWinApp::InitInstance(); AfxEnableControlContainer(); SetRegistryKey(_T("Local AppWizard-Generated Applications")); CD1Dlg dlg; // Создание объекта диалога m_pMainWnd = &dlg; INT_PTR nResponse = dlg.DoModal(); // Отображение // диалога if (nResponse == IDOK) { } else if (nResponse == IDCANCEL) { } return FALSE; // Завершение приложения }
Класс диалога имеет следующее объявление и реализацию:
// Заголовочный файл class CD1Dlg : public CDialog {public: CD1Dlg(CWnd* pParent = NULL); // Конструктор enum { IDD = IDD_D1_DIALOG }; // Ресурс диалога protected: // Поддержка DDX/DDV: virtual void DoDataExchange(CDataExchange* pDX); // Реализация protected: HICON m_hIcon;
// Функции таблицы обрабатываемых событий virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); DECLARE_MESSAGE_MAP() }; // Файл реализации D1Dlg.cpp #include "stdafx.h" #include "D1.h" #include "D1Dlg.h" class CAboutDlg : public CDialog // Класс // вспомогательного диалога {public: CAboutDlg(); enum { IDD = IDD_ABOUTBOX }; // Ресурс диалога protected: virtual void DoDataExchange(CDataExchange* pDX); protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX);} BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) END_MESSAGE_MAP() // Класс окна диалога приложения CD1Dlg::CD1Dlg(CWnd* pParent /*=NULL*/) : CDialog(CD1Dlg::IDD, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CD1Dlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CD1Dlg, CDialog) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() END_MESSAGE_MAP() // Методы обработки событий BOOL CD1Dlg::OnInitDialog() { CDialog::OnInitDialog(); ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); // Указатель // на системное меню if (pSysMenu != NULL) // Добавление пунктов к { CString strAboutMenu; //системному меню strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } SetIcon(m_hIcon, TRUE); // Определение крупной // пиктограммы SetIcon(m_hIcon, FALSE); // Определение мелкой пиктограммы return TRUE; } void CD1Dlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialog::OnSysCommand(nID, lParam); } } void CD1Dlg::OnPaint() { CDialog::OnPaint(); }
HCURSOR CD1Dlg::OnQueryDragIcon() // Запрос курсора { return static_cast
Листинг 19.1.
Для отображения окна диалога следует:
Связывание командных кнопок с методами - обработчиками событий и идентификаторов ресурсов элементов управления с переменными выполняется в редакторе ресурсов.
Для назначения кнопке метода обработчика события можно выполнить на ней двойной щелчок мышью (для события BN_CLICKED) или выделить элемент управления и выполнить команду контекстного меню Add Event Handler.
По умолчанию для командной кнопки, выполняющей завершение диалога, будет вставлен следующий метод - обработчик события:
void CD1Dlg::OnBnClickedOk() { OnOK(); // OnOK - метод базового класса CDialog }
Имя метода - обработчика события формируется из префикса On, имени события и идентификатора элемента управления.
Обработчик события добавляется как новый член класса диалога.
При добавлении каждого нового метода обработчика события в таблицу сообщений также добавляется новый вход.
Таблица сообщений (иногда называемая также таблицей событий) указывается между макросами BEGIN_MESSAGE_MAP иEND_MESSAGE_MAP. Макрос BEGIN_MESSAGE_MAP имеет два параметра - имя класса, обрабатывающего данные сообщения, и имя базового класса. Чтобы определить, что данный класс имеет таблицу сообщений, в заголовочном файле указывается макрос DECLARE_MESSAGE_MAP.
Например:
BEGIN_MESSAGE_MAP(CD1Dlg, CDialog) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDOK, &CD1Dlg::OnBnClickedOk) END_MESSAGE_MAP()
Каждый вход таблицы сообщений содержит имя события (например, ON_BN_CLICKED), идентификатор ресурса элемента управления (например, IDOK) и имя метода - обработчика события (например, &CD1Dlg::OnBnClickedOk).
Диалог может быть создан как:
Для того чтобы отобразить созданный диалог как модальный, следует создать объект диалога и выполнить для него метод DoModal().
Например:
CD1Dlg dlg; INT_PTR nResponse = dlg.DoModal();
Если предполагается определить диалог как главное окно приложения, то необходимо для объекта приложения установить значение свойства m_pMainWnd (CWnd* m_pMainWnd;).
Например:
CD1Dlg dlg; m_pMainWnd = &dlg; // AfxGetApp()->m_pMainWnd // AfxGetApp() возвращает для // приложения указатель на // объект типа CWinApp INT_PTR nResponse = dlg.DoModal();
Для модального диалога следует проверять код завершения диалога.
Например:
INT_PTR nResponse = dlg.DoModal(); if (nResponse == IDOK) { } else if (nResponse == IDCANCEL) { }
Для того чтобы отобразить диалог как немодальный, следует создать объект диалога и выполнить для него метод Create().
Например:
CMyDialog* pDialog; // Указатель на объект диалога void CMyWnd::OnSomeAction() { // pDialog может быть инициализирован как NULL // в конструкторе или классе CMyWnd pDialog = new CMyDialog(); if(pDialog != NULL) { BOOL ret = pDialog->Create(IDD_MYDIALOG,this); // Параметр // указывает используемый ресурс диалога if(!ret) AfxMessageBox("Ошибка при создании диалога"); pDialog->ShowWindow(SW_SHOW); } else AfxMessageBox("Ошибка с объектом диалога"); }
Создание приложений на С++ в Visual Studio .NET
Среда Visual Studio .NET позволяет создавать приложения с использованием различных библиотек:Стандартные диалоги
Класс CCommonDialog - это базовый класс, инкапсулирующий поведение стандартных диалогов Windows. Библиотека MFC содержит классы стандартных диалогов, производных от CCommonDialog, включая следующие:Для использования класса CCommonDialog следует подключить заголовочный файл afxdlgs.h.
Введение в программирование
Архитектура "документ-отображение"
Для реализации SDI и MDI-приложений посредством библиотеки классов MFC применяется механизм "документ/отображение". Это позволяет отображать один документ различными способами.Документы и отображения
Архитектура "документ/отображение" (document/view) базируется на взаимодействии четырех основных классов.Отображение представляет вид документа на экране и является некоторой средой, взаимосвязывающей документ и пользователя. Объект "отображение" воспроизводит вид документа на экране и интерпретирует действия пользователя как операции над документом. Класс отображения также форматирует изображение как для печати, так и для предварительного просмотра.

Рис. 20.1. Взаимосвязь объектов "документ ->отображение -> окно-рамка "
Объект "отображение" используется не только для представления документов на экране. Он же представляет документ и для печати, и для предварительного просмотра печатаемого документа.
С помощью механизма "документ-отображение" реализуется разделение данных, их экранное представление и обработка действий пользователя.
Операции по изменению данных реализуются классами документа.
Объект отображение только вызывает этот интерфейс для доступа и обновления данных.
Документы, сопоставленные им отображения, окна-рамки создаются с использованием шаблона документа. Каждый шаблон документа используется для создания всех документов одного типа и управления ими.
Создание нового документа и сопоставленного ему отображения и окна-рамки. выполняется различными объектами: объектом "приложение", шаблоном документа, созданным документом и созданным окном-рамкой.
Следующая таблица иллюстрирует, какими объектами создаются шаблон документа, документ, окно-рамка и отображение.
| Приложение (Application object) | Шаблон документа |
| Шаблон документа (Document template) | Документ |
| Шаблон документа (Document template) | Окно-рамка |
| Окно-рамка (Frame window) | Отображение (View) |
Объект "документ" отвечает за внутреннее представление данных, показываемых объектом "отображене". Объект "отображение" предназначен для манипулирования данными объекта документа. Объект "отображение" состыковывается с объектом "документ" и окном-рамкой, образуя цепочку "документ->отображение->окно". Приложения, использующие SDI (Single Document Interface) или MDI (Multi Document Inter-face) интерфейс соответственно, называются SDI-приложением и MDI-приложением. Такие приложения относятся к приложениям с архитектурой "документ-отображение" (document/view).
включает также код класса CAboutDlg
|
// App1.cpp : Файл реализации класса приложения // ( включает также код класса CAboutDlg для диалога About) // Таблица сообщений класса CApp1App BEGIN_MESSAGE_MAP(CApp1App, CWinApp) ON_COMMAND(ID_APP_ABOUT, OnAppAbout) ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup) END_MESSAGE_MAP() CApp1App::CApp1App() { } // Конструктор CApp1App theApp; // Объект приложение BOOL CApp1App::InitInstance() // Первый выполняемый метод //приложения { CWinApp::InitInstance(); // Регистрация шаблонов документа CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( // Строка //описания шаблона IDR_MAINFRAME, RUNTIME_CLASS(CApp1Doc), RUNTIME_CLASS(CMainFrame), // Главное окно // SDI приложения RUNTIME_CLASS(CApp1View)); AddDocTemplate(pDocTemplate); m_pMainWnd->ShowWindow(SW_SHOW); // Отображение // главного окна приложения m_pMainWnd->UpdateWindow(); return TRUE; } // Класс CAboutDlg, реализующий диалог About class CAboutDlg : public CDialog {public: CAboutDlg(); enum { IDD = IDD_ABOUTBOX }; // Идентификатор ресурса диалога protected: virtual void DoDataExchange(CDataExchange* pDX); protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX);} BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) // Таблица сообщений END_MESSAGE_MAP() void CApp1App::OnAppAbout() // Обработчик команды меню About { CAboutDlg aboutDlg; aboutDlg.DoModal(); // Отображение модального диалога } // App1Doc.cpp : файл реализации класса документа CApp1Doc IMPLEMENT_DYNCREATE(CApp1Doc, CDocument) BEGIN_MESSAGE_MAP(CApp1Doc, CDocument) // Таблица сообщений END_MESSAGE_MAP() CApp1Doc::CApp1Doc() {} CApp1Doc::~CApp1Doc() { } BOOL CApp1Doc::OnNewDocument() // Переопределение метода, { // вызываемого при открытии документа if (!CDocument::OnNewDocument()) return FALSE; // Место для добавления кода инициализации документа return TRUE; } void CApp1Doc::Serialize(CArchive& ar) // Сериализация { if (ar.IsStoring()) { // Место для добавления кода сохранения } else { // Место для добавления кода загрузки } } // App1View.cpp : файл реализации класса CApp1View IMPLEMENT_DYNCREATE(CApp1View, CView) BEGIN_MESSAGE_MAP(CApp1View, CView) // Таблица сообщений ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) END_MESSAGE_MAP() CApp1View::CApp1View() { } CApp1View::~CApp1View() { } BOOL CApp1View::PreCreateWindow(CREATESTRUCT& cs) { return CView::PreCreateWindow(cs); } void CApp1View::OnDraw(CDC* /*pDC*/) { CApp1Doc* pDoc = GetDocument(); // Указатель на документ ASSERT_VALID(pDoc); } BOOL CApp1View::OnPreparePrinting(CPrintInfo* pInfo) { return DoPreparePrinting(pInfo); // Действие по умолчанию } void CApp1View::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { } void CApp1View::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { } // MainFrm.cpp : файл реализации класса CMainFrame IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd) BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //Таблица сообщений ON_WM_CREATE() END_MESSAGE_MAP() static UINT indicators[] = { ID_SEPARATOR, // Идентификаторы ресурсов ID_INDICATOR_CAPS, // для строки состояния ID_INDICATOR_NUM, ID_INDICATOR_SCRL, }; CMainFrame::CMainFrame() { } CMainFrame::~CMainFrame() { } // Обработчик сообщения, получаемого при создания окна int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; return 0; } BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { if( !CFrameWnd::PreCreateWindow(cs) ) return FALSE; return TRUE; } |
| Листинг 20.1. |
| Закрыть окно |
// App1.cpp : Файл реализации класса приложения
// ( включает также код класса CAboutDlg для диалога About)
// Таблица сообщений класса CApp1App
BEGIN_MESSAGE_MAP(CApp1App, CWinApp)
ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
ON_COMMAND(ID_FILE_PRINT_SETUP,
CWinApp::OnFilePrintSetup)
END_MESSAGE_MAP()
CApp1App::CApp1App() { } // Конструктор
CApp1App theApp; // Объект приложение
BOOL CApp1App::InitInstance() // Первый выполняемый метод //приложения
{ CWinApp::InitInstance();
// Регистрация шаблонов документа
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate( // Строка
//описания шаблона
IDR_MAINFRAME,
RUNTIME_CLASS(CApp1Doc),
RUNTIME_CLASS(CMainFrame), // Главное окно
// SDI приложения
RUNTIME_CLASS(CApp1View));
AddDocTemplate(pDocTemplate);
m_pMainWnd->ShowWindow(SW_SHOW); // Отображение
// главного окна приложения
m_pMainWnd->UpdateWindow();
return TRUE;
}
// Класс CAboutDlg, реализующий диалог About
class CAboutDlg : public CDialog
{public:
CAboutDlg();
enum { IDD = IDD_ABOUTBOX }; // Идентификатор ресурса диалога
protected:
virtual void DoDataExchange(CDataExchange* pDX);
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { }
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{ CDialog::DoDataExchange(pDX);}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) // Таблица сообщений
END_MESSAGE_MAP()
void CApp1App::OnAppAbout() // Обработчик команды меню About
{ CAboutDlg aboutDlg;
aboutDlg.DoModal(); // Отображение модального диалога
}
// App1Doc.cpp : файл реализации класса документа CApp1Doc
IMPLEMENT_DYNCREATE(CApp1Doc, CDocument)
BEGIN_MESSAGE_MAP(CApp1Doc, CDocument) // Таблица сообщений
END_MESSAGE_MAP()
CApp1Doc::CApp1Doc() {}
CApp1Doc::~CApp1Doc() { }
BOOL CApp1Doc::OnNewDocument() // Переопределение метода,
{ // вызываемого при открытии документа
if (!CDocument::OnNewDocument())
Класс CDocTemplate
Класс CDocTemplate является абстрактным классом, предоставляющим основные возможности для работы с шаблонами документов.Шаблон документа определяет отношение между тремя типами классов.
Для каждого типа документа в приложении создается отдельный шаблон документа.
Класс CDocTemplate является абстрактным классом и, следовательно, не может использоваться непосредственно для создания объектов. Обычно для создания шаблонов применяются его производные классы CSingleDocTemplate и CMultiDocTemplate. Однако можно создать и свой собственный производный класс шаблона документа.
Например:
BEGIN IDR_MYTYPE "\nSheet\nWorksheet\n Worksheets (*.myc)\n.myc\ n MyCalcSheet\nMyCalc Worksheet" END BOOL CMyApp::InitInstance() { CMultiDocTemplate* pDocTemplate; pDocTemplate = new CMultiDocTemplate( IDR_MYTYPE, RUNTIME_CLASS(CMyDoc), RUNTIME_CLASS(CMDIChildWnd), RUNTIME_CLASS(CMyView)); AddDocTemplate(pDocTemplate); // ... }
Класс CDocument
Класс CDocument предоставляет основные возможности управления документами.Класс CDocument поддерживает набор стандартных операций над документом: создание, загрузка, сохранение.
Каждый документ содержит указатель на сопоставленный ему объект шаблона документа - для каждого типа документа; свой шаблон.
Пользователи взаимодействуют с документом посредством объекта отображение (наследуемого от класса Cview - или его производных классов), ассоциированного с документом.
Для работы с документами в приложении следует:
Класс CFormView
Класс CFormView является базовым классом, используемым для отображений, которые содержат элементы управления. Процесс создания отображения CFormView аналогичен созданию диалогового окна.Для того чтобы использовать отображение CFormView, следует:
CMyFormView::CMyFormView() : CFormView( CMyFormView::IDD );
Класс CMultiDocTemplate
Класс CMultiDocTemplate определяет шаблон документа для MDI-приложений: одновременно может быть открыто несколько документов; главное окно используется как пространство, в котором можно открывать несколько окон-рамок для отображения документов.Класс CSingleDocTemplate
Класс CSingleDocTemplate определяет шаблон документа для SDI-приложений: одновременно может быть открыт только один документ; документ отображается в главном окне. Как правило, SDI-приложения поддерживают только один тип документа и, соответственно, имеют только один объект CSingleDocTemplate.Класс CView
Класс CView реализует основные возможности для работы с отображениями. Отношения между классом отображения, классом окна-рамки и классом документа устанавливаются объектом CDocTemplate. Вызов конструктора нового отображения и сопоставление его документу выполняется при открытии пользователем нового окна или при разделении существующего окна.Один объект "отображение" может быть сопоставлен только одному документу, однако один документ может иметь несколько сопоставленных ему отображений.
Отображения обрабатывают сообщения, зафиксированные в таблице сообщений.
Отображения выполняют только показ данных документа и не отвечают за сохранение данных. Документ предоставляет отображению необходимую информацию о данных посредством вызова объектом "отображение" методов документа. Для инициализации отображения, сопоставленного документу, следует вызвать метод OnInitialUpdate .
Для внесения изменений в данные отображение, как правило, вызывает метод CDocument::UpdateAllViews, который инициирует вызов методов OnUpdate для всех других отображений. По умолчанию реализация метода OnUpdateопределяет всю клиентскую область как недостоверную. Этот метод может быть переопределен.
Для того чтобы использовать класс отображений Cview, следует:
В классе View объявлены методы для реализации архитектуры "документ-отображение", включая следующие:
Классы отображений
Для вывода данных на различные графические устройства используются классы отображений. Базовым классом всех таких классов является класс Cview, производный от класса CWnd.Библиотека MFC, кроме класса Cview, предоставляет широкий набор классов отображения, наследуемых от CView, включая следующие классы:
Реализация интерфейса пользователя
Классы отображений содержат набор методов, которые выполняют обработку событий, инициированных пользователем. Переопределение этих методов позволяет задавать:Реализация интерфейса пользователя может включать в себя обработку:
MFC поддерживает реализацию трех типов интерфейса для отображения одного документа посредством нескольких отображений:



Поддержка работы с разделенным окном реализована в классе CSplitterWnd.Разделенным окном является обычное окно, разбитое на несколько панелей (окон) и содержащее один и тот же документ.
Цикл жизни документа (а совместно с ним - и окна-рамки, и отображения) в MDI-приложении состоит из:
Цикл жизни документа (а совместно с ним - и окна-рамки и отображения) в SDI-приложении отличается тем, что вызов конструктора происходит только один раз при первоначальном создании документа; аналогично и проходит вызов деструктора. А для каждого повторно создаваемого или открываемого документа выполняются только этапы 2-3-4.
Сериализация данных
Библиотека MFC реализует модель обмена данными между документом и файлом через специальный объект, называемый архивом. Обмен данными между приложением и архивом называется сериализацией. Для обмена используется метод Serialize класса документа.При создании шаблона приложения с помощью мастера AppWizard можно добавить меню File, содержащее пункты Open, Save и Save as. Для обработки каждого из указанных пунктов меню, AppWizard вставляет в программный код класса документа (производного от CDocument) переопределяемый метод Serialize - либо для чтения состояния документа из архива, либо для записи (загрузки) состояния документа в архив. Программисту необходимо только вставить в метод Serialize код, выполняющий запись переменных в архив и чтение переменных из архива.
Для использования средств сериализации следует включить в объявление и реализацию класса макросы DECLARE_SERIAL и IMPLEMENT_SERIAL соответственно.
Создание приложения с архитектурой "документ-отображение"
При создании приложения, использующего архитектуру "документ-отображение", как правило, должны быть выполнены следующие шаги:Создание SDI-приложения
Начинать разработку приложения следует с формирования "макета" приложения, который автоматически формируется мастером приложений AppWizard.Мастер приложений MFC Application Wizardпозволяет создавать следующие шаблоны для приложений с архитектурой "документ-отображение":
Мастер приложений позволяет последовательно формировать шаблон SDI приложения (Single Document Interface), включая или не включая в него поддержку таких элементов, как: доступ к информации из баз данных, реализация возможностей контейнера или сервера, применение OLE, использование элементов ActiveX, а также реализацию встроенной панели инструментов, строки состояния, некоторых пунктов меню.
Так, при формировании SDI приложения будут созданы следующие классы:
Далее приводится код основных фрагментов SDI-приложения, автоматически создаваемого мастером MFC Application Wizard.
// App1.cpp : Файл реализации класса приложения // (включает также код класса CAboutDlg для диалога About) // Таблица сообщений класса CApp1App BEGIN_MESSAGE_MAP(CApp1App, CWinApp) ON_COMMAND(ID_APP_ABOUT, OnAppAbout) ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew) ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup) END_MESSAGE_MAP() CApp1App::CApp1App() { } // Конструктор CApp1App theApp; // Объект приложение BOOL CApp1App::InitInstance() // Первый выполняемый метод //приложения { CWinApp::InitInstance(); // Регистрация шаблонов документа CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( // Строка //описания шаблона IDR_MAINFRAME, RUNTIME_CLASS(CApp1Doc), RUNTIME_CLASS(CMainFrame), // Главное окно // SDI приложения RUNTIME_CLASS(CApp1View)); AddDocTemplate(pDocTemplate);
m_pMainWnd->ShowWindow(SW_SHOW); // Отображение // главного окна приложения m_pMainWnd->UpdateWindow(); return TRUE; } // Класс CAboutDlg, реализующий диалог About class CAboutDlg : public CDialog {public: CAboutDlg(); enum { IDD = IDD_ABOUTBOX }; // Идентификатор ресурса диалога protected: virtual void DoDataExchange(CDataExchange* pDX); protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX);} BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) // Таблица сообщений END_MESSAGE_MAP() void CApp1App::OnAppAbout() // Обработчик команды меню About { CAboutDlg aboutDlg; aboutDlg.DoModal(); // Отображение модального диалога } // App1Doc.cpp : файл реализации класса документа CApp1Doc IMPLEMENT_DYNCREATE(CApp1Doc, CDocument) BEGIN_MESSAGE_MAP(CApp1Doc, CDocument) // Таблица сообщений END_MESSAGE_MAP() CApp1Doc::CApp1Doc() {} CApp1Doc::~CApp1Doc() { } BOOL CApp1Doc::OnNewDocument() // Переопределение метода, { // вызываемого при открытии документа if (!CDocument::OnNewDocument()) return FALSE; // Место для добавления кода инициализации документа return TRUE; } void CApp1Doc::Serialize(CArchive& ar) // Сериализация { if (ar.IsStoring()) { // Место для добавления кода сохранения } else { // Место для добавления кода загрузки } } // App1View.cpp : файл реализации класса CApp1View IMPLEMENT_DYNCREATE(CApp1View, CView) BEGIN_MESSAGE_MAP(CApp1View, CView) // Таблица сообщений ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) END_MESSAGE_MAP() CApp1View::CApp1View() { } CApp1View::~CApp1View() { } BOOL CApp1View::PreCreateWindow(CREATESTRUCT& cs) { return CView::PreCreateWindow(cs); } void CApp1View::OnDraw(CDC* /*pDC*/) { CApp1Doc* pDoc = GetDocument(); // Указатель на документ ASSERT_VALID(pDoc); } BOOL CApp1View::OnPreparePrinting(CPrintInfo* pInfo) { return DoPreparePrinting(pInfo); // Действие по умолчанию } void CApp1View::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { } void CApp1View::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/) { } // MainFrm.cpp : файл реализации класса CMainFrame IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd) BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //Таблица сообщений ON_WM_CREATE() END_MESSAGE_MAP() static UINT indicators[] = { ID_SEPARATOR, // Идентификаторы ресурсов ID_INDICATOR_CAPS, // для строки состояния ID_INDICATOR_NUM, ID_INDICATOR_SCRL, }; CMainFrame::CMainFrame() { } CMainFrame::~CMainFrame() { } // Обработчик сообщения, получаемого при создания окна int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; return 0; } BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) { if( !CFrameWnd::PreCreateWindow(cs) ) return FALSE; return TRUE; }
Листинг 20.1.

Управление документом
Для реализации управления данными документа первоначально необходимо выполнить следующие шаги:Данные документа хранятся в переменных класса документа. Библиотека MFC содержит ряд классов, инкапсулирующих работу с различными наборами данных, включая следующие классы:
Для выполнения операций над элементами данных в класс документа добавляются требуемые методы.
При создании объекта "отображение" формируется указатель на документ, используемый отображением для доступа к объекту "документ" (его методам и переменным). Этот указатель может быть получен объектом отображения вызовом метода GetDocument класса CView. Отображение использует данные, хранимые в классе документа, для их отображения и изменения.
Введение в программирование
Элементы управления
Библиотека MFC содержит широкий набор классов элементов управления. Элементы управления, отображаемые любым окном, предварительно должны быть добавлены в ресурс диалога. Это выполняется в редакторе ресурсов среды проектирования Visual Studio .NET.Все оконные элементы управления наследуются от класса CWnd. На следующей схеме представлен список классов элементов управления, наследуемых от CWnd.

В окне Resource View все ресурсы каждого проекта отображаются в виде иерархического дерева с узлами, соответствующими типам используемых ресурсов. В каждом узле расположены ресурсы одного типа. Так, узел Dialog содержит все ресурсы-диалоги, используемые в приложении.
Каждый ресурс имеет свой идентификатор ресурса, используемый функциями библиотеки MFC для доступа к данному ресурсу.
Класс CAnimateCtrl
Класс CAnimateCtrl предоставляет функции управления анимацией в среде Windows.Элемент управления, созданный как экземпляр данного класса, является объектом анимации. Такой объект представляет из себя обычное окно, в котором отображается некоторый клип, являющийся файлом в формате AVI (Audio Video Interleaved). AVI-файл содержит последовательный набор битовых изображений. Объекты анимации могут показывать только простые AVI-клипы.
Класс CButton
Класс CButton применяется для работы со следующими элементами управления:Элемент управления "кнопка", называемый также командная кнопка, как правило, используется для обработки сообщения BN_CLICKED.
Свойство Default Button позволяет указать командную кнопку, устанавливаемую как кнопка по умолчанию: нажатие пользователем клавиши Enter интерпретируется как щелчок на данной кнопке.
Свойство Icon позволяет указать, что вместо текста будет отображена пиктограмма (стиль BS_ICON).
Свойство Bitmap позволяет указать, что вместо текста будет отображено изображение (стиль BS_BITMAP).
Свойство Multiline используется в том случае, если текст слишком длинный, чтобы уместиться на кнопке в одну строку.
Для работы с элементом управления "кнопка" следует использовать класс CButton.
Элемент управления "флажок Check Box" может иметь два или три состояния: включенное, выключенное или неопределенное (необязательно).
Кроме возможности связывания флажка с объектом типа CButton, флажок можно связать с переменной типа BOOL.
Свойство Auto позволяет создавать элемент управления, переключение состояний (включен/выключен) которого происходит автоматически при щелчке мышью.
Свойство Tri-state используется для создания элемента управления "флажок", имеющего три состояния. Одновременно с состояниями "включен" или "выключен", используется состояние "неопределен", в котором флажок выглядит "посеревшим". Если значение свойства Push Like установлено равным True, то создается элемент управления "флажок", отображаемый как командная кнопка: при нажатии флажок имеет вид вдавленной кнопки, а при ненажатом состоянии - выпуклой.
Свойство Flat определяет, будет ли создаваемый элемент управления иметь вид плоской кнопки.
Элемент управления "радиокнопка" (Radio Button) аналогичен флажку, но не может иметь неопределенного состояния. Также при объединении несколько радиокнопок в группу только одна из них может иметь включенное состояние.
Кроме возможности связывания флажка с объектом типа CButton, радиокнопку можно связать с переменной типа BOOL.
При использовании класса CButton тип кнопки можно определить ее стилем, указываемым в методе Create при создании кнопки. Класс CButton наследуется классом CBitmapButton, реализующим кнопки с изображениями вместо текста.
Создать кнопку можно как с использованием редактора ресурсов, так и непосредственно программным путем.
Если объект CButton создается расположенным в диалоговом окне, то он автоматически разрушается при закрытии пользователем этого диалогового окна.
Если же объект был создан динамически вызовом метода new, то для его разрушения следует вызвать метод delete.
Класс CButton предоставляет ряд методов, включая следующие:
Для командных кнопок метод возвращает значение 0.
Например:
CButton myButton; // Создание кнопки с изображением myButton.Create(_T("Кнопка 1"), WS_CHILD|WS_VISIBLE|BS_BITMAP, CRect(10,10,60,50), pParentWnd, 1); myButton.SetBitmap( ::LoadBitmap(NULL, MAKEINTRESOURCE(OBM_CHECK)) );
Класс CComboBox
Класс CComboBox реализует функционирование комбинированного окна, иногда также называемого комбинированным окном списка.Комбинированное окно представляет собой элемент управления, объединяющий окно списка с окном редактирования или со статическим элементом управления. Окно списка при этом может иметь как распахнутое (отображаемое постоянно), так и свернутое состояние (отображаемое только при щелчке пользователя на стрелке вниз).
Выбранный элемент списка отображается в окне редактирования или в окне статического элемента управления.
Метод GetLBText определяет строку текста, соответствующую указанному индексу элемента окна списка комбинированного окна. Метод GetCurSel возвращает индекс выделенного элемента окна списка комбинированного окна. Если выделенного элемента нет, то метод возвращает значение CB_ERR.
Например:
extern CComboBox* pmyComboBox; // Выбор следующего элемента в окне комбинированного списка // после текущего элемента int nIndex = pmyComboBox->GetCurSel(); // Индекс текущего // элемента int nCount = pmyComboBox->GetCount(); // Всего элементов // в списке if ((nIndex != CB_ERR) && (nCount > 1)) { if (++nIndex < nCount) pmyComboBox->SetCurSel(nIndex); else pmyComboBox->SetCurSel(0); }
Класс CEdit
Класс CEdit обеспечивает функционирование элемента управления "окно редактирования", называемое также текстовым полем. Окно редактирования может быть как однострочным, так и многострочным. Иногда однострочное окно редактирования также называют полем ввода.Для создания многострочного окна редактирования следует установить значение свойства Multilin равным True. Для установки разбиения на строки с использованием мягкого конца строки следует установить свойство Auto HScroll.
При мягком конце строки, в отличие от жесткого, окно редактирования отображает разделение на строки, не вставляя непосредственно в сам текст символы конца строки и перевода каретки.
При двойном щелчке мыши на элементе управления выполняется автоматическая вставка метода обработчика сообщения OnEnChangeEdit.
Таблица сообщений родительского окна может содержать для элемента управления "окно редактирования" вызовы следующих стандартных обработчиков сообщений:
Класс CEdit предоставляет широкий набор методов для работы с окном редактирования, включая следующие:
Например:
extern CEdit* pmyEdit; int i, nLineCount = pmyEdit->GetLineCount(); CString strText, strLine; for (i=0;i < nLineCount;i++) { // Получение длины строки i int len = pmyEdit->LineLength(pmyEdit->LineIndex(i)); pmyEdit->GetLine(i, strText.GetBuffer(len), len); strText.ReleaseBuffer(len); strLine.Format(TEXT("line %d: '%s'\n"), i, strText); cout<< strLine; }
Например:
extern CEdit* pmyEdit; // Выделить все символы, следующие за текущим // выделенным фрагментом DWORD dwSel = pmyEdit->GetSel(); // Определение // позиций текущего выделения pmyEdit->SetSel(HIWORD(dwSel), -1); // HIWORD(dwSel) - // позиция первого невыделенного символа, // следующего за выделением
Класс CListBox
Элемент управления List Box "окно списка", называемый иногда просто списком, используется для работы с информацией, отображаемой в виде списка. Окно списка может быть реализовано как список с единичным или с множественным выбором.Окно списка можно связать как с переменной типа CString, так и с объектом класса CListBox.
Свойство Selection позволяет устанавливать тип списка:
Класс CListBox предоставляет широкий набор методов для работы со списком, включая следующие:
Например:
extern CListBox* pmyListBox; // Определение первым видимым элементом списка // элемента,расположенного посередине списка pmyListBox->SetTopIndex(pmyListBox->GetCount()/2);
Например:
extern CListBox* pmyListBox; CString str, str2; int n; for (int i=0;i < pmyListBox->GetCount();i++) { n = pmyListBox->GetTextLen( i ); pmyListBox->GetText( i, str.GetBuffer(n) ); str.ReleaseBuffer(); // Дампинг всех элементов списка (#ifdef _DEBUG ) str2.Format(_T("item %d: %s\r\n"), i, str.GetBuffer(0)); afxDump << str2; }
Класс CRichEditCtrl
Элемент управления "окно расширенного редактирования" (rich edit control) позволяет пользователю не только редактировать текст, в отличие от элемента управления "окно редактирования", но также выполнять форматирование символов и абзацев, встраивать OLE-объекты.Данный элемент управления обеспечивает программный интерфейс для форматирования текста, при этом в приложении должны быть реализованы все компоненты интерфейса пользователя, делающие доступными сами операции форматирования.
Метод GetDefaultCharFormat позволяет получить атрибуты форматирования символов по умолчанию. Атрибуты форматирования символов определяются в структуре типа CHARFORMAT, а атрибуты форматирования абзаца задаются в структуре типа PARAFORMAT. Метод GetLine копирует в буфер указанную строку текста.
Методы SetDefaultCharFormat и SetSelectionCharFormat позволяют установить атрибуты форматирования символов, используемые по умолчанию или для выделенного фрагмента текста соответственно.

Класс CStatic
Класс CStatic реализует работу со статическим текстом (меткой). Этот элемент управления предназначается для отображения различных надписей и не может непосредственно редактироваться пользователем. Содержание элемента управления Static Box определяется на этапе проектирования свойством Caption и в дальнейшем может изменяться только программным путем.Выполнить связывание элемента управления "статический текст" можно с переменной типа CString или объектом класса CStatic.
Свойство Border определяет, что вокруг элемента управления будет отображаться рамка.
Свойство Transparent определяет, является ли фон элемента прозрачным.
Работа с элементами управления
Каждый элемент управления имеет свой набор атрибутов, называемых также свойствами элемента управления.Изменение атрибутов элемента управления в процессе выполнения приложения происходит или под воздействием пользователя на данный элемент управления, или программным путем.
Так как все элементы управления реализуются классами, производными от CWnd, то они являются оконными объектами. Как и для всех оконных объектов, процесс создания включает в себя два этапа:
Если элемент управления не требуется изменять в процессе выполнения приложения, то для него можно не выполнять связывание с объектом или с переменной.
Все элементы управления, встраиваемые в диалоговые окна, можно подразделить на общие и ActiveX-элементы управления. Библиотека MFC поддерживает как те, так и другие, инкапсулируя их свойства и поведение в соответствующих классах.
Инициализацию элементов управления можно выполнять в методе OnInitDialog класса диалога.
Чтобы изменять значения, отображаемые элементами управления, или использовать значения, определяемые посредством действий пользователя, приложению нужно иметь доступ к элементам управления.
Доступ к элементам управления может быть реализован различными способами:
Например:
// 1. Инициализация указателем: CEdit* pEdit1 = (CEdit*) GetDlgItem(IDC_EDIT1); // Конструкция pMyStat-> Metod() позволяет вызывать // любые методы класса // 2.
Доступ по идентификатору: SetDlgItemText(IDC_EDIT1, L"12345");
Для связывания элемента управления с переменной или объектом соответствующего класса можно использовать диалог Add Member Variable Wizard (рис. 21.4), вызываемый командой контекстного меню Add Variable.

Рис. 21.4. Диалог Add Member Variable
Флажок Control variable определяет способ использования создаваемой переменной:
Редактор ресурсов
В редакторе ресурсов диалога можно выполнить настройку внешнего вида и поведение диалога, а также всех элементов управления, расположенных в нем.Для задания значений свойств и указания используемых обработчиков событий в редакторе ресурсов используется окно свойств Properties. Оно состоит из двух страниц: страницы свойств и страницы событий (рис. 21.1). Каждая станица отображается как четыре вертикально расположенных области. В первой верхней области расположен список имен объектов, содержащий идентификаторы всех используемых для данного диалога элементов управления, включая и идентификатор ресурса диалога.

Рис. 21.1. Окно свойств редактора ресурсов — страницы Properties и Events
Под списком имен объектов расположена панель инструментов, содержащая кнопки: для выбора типа просмотра свойств (по категориям или в алфавитном порядке), переключения между страницей свойств Properties, страницей событий для элементов управления Control Events и страницей сообщений Messages (только для диалога).
В следующей области располагается панель значений - страница Properties или страница Control Events. В нижней части окна свойств Properties расположено поле, отображающее текущий выделенный элемент страницы свойств или страницы событий.
На странице событий представлены идентификаторы всех элементов управления диалога и список допустимых для каждого элемента управления сообщений. Если обработчик сообщения создан, то в поле справа от имени сообщения указывается имя метода - обработчика данного сообщения.
При создании посредством окна свойств нового метода - обработчика события в заголовочный файл будет добавлено объявление нового метода - обработчика события, в файл реализации вставлен код описания метода, а в таблицу сообщений - новый вход для данного сообщения.
Доступные элементы управления отображаются в редакторе ресурсов в окне Toolbox . Для того чтобы расположить элемент управления в диалоге, достаточно выделить этот компонент на вкладке Dialog Editor окна Toolbox , а затем щелкнуть мышью в требуемом месте разрабатываемого диалогового окна.
В шаблон диалога можно встроить любой элемент управления, расположенный в окне Toolbox (рис. 21.2).

Рис. 21.2. Панель элементов управления
Вкладка Dialog Editor окна Toolbox содержит кнопки для следующих элементов управления (сверху вниз):
Список отображаемых элементов управления в окне Toolbox можно редактировать. Для этого следует выполнить команду меню Tools | Choose Toolbox Items и выбрать добавляемые элементы управления (рис. 21.3).

Рис. 21.3. Диалог Choose Toolbox Items
Введение в программирование
Класс CImageList
Класс CImageList реализует работу со списком одноразмерных изображений. Его непосредственным базовым классом является класс CObject.Методы класса CImageList позволяют более эффективно управлять наборами больших и малых пиктограмм и наборами изображений.
Все изображения списка изображений хранятся в одном битовом массиве в экранном формате. Дополнительно список изображений может включать монохромный битовый массив, который содержит маску, используемую для отображения изображений с прозрачным фоном.
Пиктограммы всегда содержат маску для отображения с прозрачным фоном.
Создание объекта "список изображений" выполняется в два этапа.
Например:
extern CImageList* pmyImageList; pmyImageList->Create(32, 32, // Размер изображения ILC_COLOR16, // 16-битовый цвет 0, // Первоначальное количество изображений 4);
Класс CImageList предоставляет ряд переменных и методов, включая следующие:
Например:
extern CImageList* pmyImageList; // Добавление к списку изображений двух пиктограмм pmyImageList->Add(AfxGetApp()->LoadIcon(IDI_ICON1)); pmyImageList->Add(AfxGetApp()->LoadIcon(IDI_ICON2)); // Добавление изображения, в котором все черные // пиксели устанавливаются прозрачными CBitmap bm; bm.LoadBitmap(IDB_BITMAP1); pmyImageList->Add(&bm, RGB(0, 0, 0));

Класс CPropertySheet
Класс CPropertySheet инкапсулирует возможности управления многостраничным диалогом.Класс CPropertySheet предоставляет ряд методов, включая следующие:
Например:
BOOL CMySheet::OnInitDialog() // Класс CMySheet //наследует от CPropertySheet { BOOL bResult = CPropertySheet::OnInitDialog(); CFrameWnd* frame = (CFrameWnd*) AfxGetMainWnd(); CPSheetDoc* doc = (CPSheetDoc*) frame->GetActiveDocument(); SetActivePage(doc->m_LastActivePage); // Устанавливаем // текущей последнюю активную страницу return bResult; } BOOL CMyPropertySheet::OnCommand(WPARAM wParam, LPARAM lParam) { if (LOWORD(wParam) == IDOK) { CFrameWnd* frame = (CFrameWnd*) AfxGetMainWnd(); CPSheetDoc* doc = (CPSheetDoc*)frame->GetActiveDocument(); // Сохранение индекса последней активной страницы: doc->m_LastActivePage = GetPageIndex(GetActivePage()); // Или GetActiveIndex() } return CPropertySheet::OnCommand(wParam, lParam); }
Например:
CPropertySheet dlg; CPropertyPage page1, page2; dlg.AddPage(&page1); dlg.AddPage(&page2); dlg.SetWizardMode(); dlg.DoModal();
Класс PRopertyPage
Объекты класса CPropertyPage представляют отдельные страницы набора свойств.Непосредственным базовым классом для СPropertyPage является класс CDialog.
Класс CPropertyPage предоставляет ряд методов, включая следующие:
Создание многостраничных диалогов
Многостраничные диалоги отображаются как:Примером мастеров могут служить мастера AppWizard, используемые для создания шаблона приложения. Для управления последовательностью страниц в мастерах используются кнопки Back, Next, Finish и Cancel. Чтобы создать окно набора свойств как мастер, следует перед вызовом метода DoModal вызвать метод SetWizardMode, а для доступа к командным кнопкам использовать метод SetWizardButtons.
Многостраничный диалог может быть реализован с помощью класса CPropertySheet, производного от CWnd. Объект класса CPropertySheet называется набором свойств или окном набора свойств.
Каждая страница из набора свойств может быть реализована объектом типа CPropertyPage, производного от CDialog.
Диалоговое окно класса CPropertyPage называется страницей свойств.
Окно набора свойств представляет собой специальный вид диалогового окна, используемый для изменения атрибутов некоторого внешнего объекта.
Окно набора свойств состоит из трех частей:
Для того чтобы использовать в приложении многостраничный диалог, нужно выполнить следующие шаги:
Шаблоны диалогов могут иметь различный размер - при размещении окна набора свойств будет выбран размер наибольшего шаблона диалога.
На странице свойств окна Properties для каждого шаблона диалога следует установить значения для свойств
Для каждой добавляемой в набор свойств страницы создать по одному объекту класса, производного от CpropertyPage
Чтобы выполнить обмен данными с окном набора свойств, следует создать переменные для доступа к элементам управления страниц свойств и использовать DDX- и DDV-методы для обмена данными между элементами управления и переменными класса окна страницы свойств.
Обмен данными для окна набора свойств следует выполнять отдельно для каждой страницы свойств.
Например:
void CMyView::DoModalPropertySheet() // Создание // модального окна { // набора свойств с двумя страницами CPropertySheet propsheet; // Создание объекта "диалог" CMyPage1 page1; // Создание объекта класса, // производного от CPropertyPage CMyPage2 page2; //... page1.m_nMember1 = m_nMember1; // Обмен данными: page1.m_nMember2 = m_nMember2; // инициализация page2.m_strMember3 = m_strMember3; // переменных членов //класса propsheet.AddPage(&page1); // Добавление страницы свойств propsheet.AddPage(&page2); //... if (propsheeet.DoModal() == IDOK) { m_nMember1 = page1.m_nMember1; m_nMember2 = page1.m_nMember2; m_strMember3 = page2.m_strMember3; GetDocument()->SetModifiedFlag(); GetDocument()->UpdateAllViews(NULL); } }
Введение в программирование
Подключение используемых пространств имен using
|
namespace D1 { // Подключение используемых пространств имен using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; // ref class объявляет управляемый класс C++ public ref class Form1 : public System::Windows::Forms::Form { public: Form1(void){InitializeComponent();} protected: ~Form1(){if (components) {delete components;}} // Объявление элемента управления "кнопка": private: System::Windows::Forms::Button^ button1; // Объявление элемента управления // "поле ввода": private: System::Windows::Forms::TextBox^ textBox1; // Объявление контейнера private:System::ComponentModel::Container ^components; #pragma region Windows Form Designer generated code // Формируется дизайнером формы void InitializeComponent(void) { // Создание объекта "кнопка": this->button1 = (gcnew System::Windows::Forms::Button()); // Создание объекта "поле ввода": this->textBox1 = (gcnew System::Windows::Forms::TextBox()); this->SuspendLayout(); // Временно // приостанавливает события компоновки до // вызова метода ResumeLayout или // Задание свойств для кнопки button1 this->button1->Location = System::Drawing::Point(427, 22); this->button1->Name = L"button1"; this->button1->Size = System::Drawing::Size(75, 23); this->button1->TabIndex = 0; this->button1->Text = L"button1"; this->button1->UseVisualStyleBackColor = true; // Определение обаботчика события Click для кнопки this->button1->Click += gcnew System::EventHandler( this, &Form1::button1_Click); // Задание свойств для поля ввода textBox1 this->textBox1->Location = System::Drawing::Point(80, 25); this->textBox1->Name = L"textBox1"; this->textBox1->Size = System::Drawing::Size(100, 20); this->textBox1->TabIndex = 1; // Задание свойств для формы Form1 this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(548, 266); ..// Добавление в форму элементов управления this->Controls->Add(this->textBox1); this->Controls->Add(this->button1); this->Name = L"Form1"; this->Text = L"Form1"; this->ResumeLayout(false); this->PerformLayout(); } #pragma endregion // Обработчик события для кнопки private: System::Void button1_Click( System::Object^ sender, System::EventArgs^ e) {this->textBox1->Text = L"123456"; } }; // Конец реализации класса } // Конец пространства имен D1 |
| Листинг 23.1. |
| Закрыть окно |
namespace D1 {
// Подключение используемых пространств имен
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
// ref class объявляет управляемый класс C++
public ref class Form1 :
public System::Windows::Forms::Form
{
public:
Form1(void){InitializeComponent();}
protected:
~Form1(){if (components) {delete components;}}
// Объявление элемента управления "кнопка":
private: System::Windows::Forms::Button^ button1;
// Объявление элемента управления
// "поле ввода":
private: System::Windows::Forms::TextBox^ textBox1;
// Объявление контейнера
private:System::ComponentModel::Container ^components;
#pragma region Windows Form Designer generated code
// Формируется дизайнером формы
void InitializeComponent(void)
{ // Создание объекта "кнопка":
this->button1 =
(gcnew System::Windows::Forms::Button());
// Создание объекта "поле ввода":
this->textBox1 =
(gcnew System::Windows::Forms::TextBox());
this->SuspendLayout(); // Временно
// приостанавливает события компоновки до
// вызова метода ResumeLayout или
// Задание свойств для кнопки button1
this->button1->Location =
System::Drawing::Point(427, 22);
this->button1->Name = L"button1";
this->button1->Size = System::Drawing::Size(75, 23);
this->button1->TabIndex = 0;
this->button1->Text = L"button1";
this->button1->UseVisualStyleBackColor = true;
// Определение обаботчика события Click для кнопки
this->button1->Click += gcnew System::EventHandler(
this,
&Form1::button1_Click);
// Задание свойств для поля ввода textBox1
this->textBox1->Location =
System::Drawing::Point(80, 25);
this->textBox1->Name = L"textBox1";
this->textBox1->Size =
System::Drawing::Size(100, 20);
this->textBox1->TabIndex = 1;
Clean up any resources being
|
namespace D_1 {partial class Form1 { private System.ComponentModel.IContainer components = null; /// #region Windows Form Designer generated code /// |
| Листинг 23.2. |
| Закрыть окно |
namespace D_1
{partial class Form1
{
private System.ComponentModel.IContainer
components = null;
///
/// Clean up any resources being used.
///
/// true if managed resources should be disposed; otherwise, false.
protected override void Dispose(bool disposing)
{ if (disposing && (components != null))
{ components.Dispose(); }
base.Dispose(disposing);
}
#region Windows Form Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.textBox1 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
// Элемент управления button1
this.button1.Location =
new System.Drawing.Point(188,27);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click +=
new System.EventHandler(this.button1_Click);
// Элемент управления textBox1
this.textBox1.Location =
new System.Drawing.Point(42, 30);
this.textBox1.Name = "textBox1";
this.textBox1.Size =
new System.Drawing.Size(100, 20);
this.textBox1.TabIndex = 1;
// Окно формы Form1
this.AutoScaleDimensions = // Определяет размер
// окна как ширина и высота (тип float)
new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode =
System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(292, 266);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.button1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
this.PerformLayout(); // Заставляет элемент
// управления выполнить для всех дочерних
// элементов компановку в соответствии
// с их свойствами
}
#endregion
// Объявление переменных
private System.Windows.Forms.Button button1;
private System.Windows.Forms.TextBox textBox1;
}
}
с новым окном подменю MenuStrip
|
// Создание объекта MenuStrip с новым окном подменю MenuStrip ms = new MenuStrip(); // Создание подменю ToolStripMenuItem windowMenu = new ToolStripMenuItem("Window"); ToolStripMenuItem windowNewMenu = new ToolStripMenuItem("New", null, // Обработчик данной команды: new EventHandler(windowNewMenu_Click)); // Список элементов меню windowMenu.DropDownItems.Add(windowNewMenu); ((ToolStripDropDownMenu) (windowMenu.DropDown)).ShowImageMargin = false; ((ToolStripDropDownMenu) (windowMenu.DropDown)).ShowCheckMargin = true; // Указывает, что ToolStripMenuItem будет отображать // список дочерних форм ms.MdiWindowListItem = windowMenu; // Добавление окна ToolStripMenuItem к линейке меню ms.Items.Add(windowMenu); // Встраивание линейки меню в верх формы. ms.Dock = DockStyle.Top; // Свойство Form.MainMenuStrip определяет линейку меню this.MainMenuStrip = ms; |
| Листинг 23.3. |
| Закрыть окно |
// Создание объекта MenuStrip с новым окном подменю
MenuStrip ms = new MenuStrip();
// Создание подменю
ToolStripMenuItem windowMenu =
new ToolStripMenuItem("Window");
ToolStripMenuItem windowNewMenu =
new ToolStripMenuItem("New",
null,
// Обработчик данной команды:
new EventHandler(windowNewMenu_Click));
// Список элементов меню
windowMenu.DropDownItems.Add(windowNewMenu);
((ToolStripDropDownMenu)
(windowMenu.DropDown)).ShowImageMargin = false;
((ToolStripDropDownMenu)
(windowMenu.DropDown)).ShowCheckMargin = true;
// Указывает, что ToolStripMenuItem будет отображать
// список дочерних форм
ms.MdiWindowListItem = windowMenu;
// Добавление окна ToolStripMenuItem к линейке меню
ms.Items.Add(windowMenu);
// Встраивание линейки меню в верх формы.
ms.Dock = DockStyle.Top;
// Свойство Form.MainMenuStrip определяет линейку меню
this.MainMenuStrip = ms;
Класс Form
Класс Systems.Windows.Forms.Form используется для создания окна формы.Формы позволяют отображать различные типы окон.
Для создания MDI-приложения можно в пустой проект добавить новый элемент MDI Parent.
Для создания формы, идентичной существующей, в проект следует добавить элемент Inherited Form и указать класс копируемой формы.
Применение стандартных диалогов
Для открытия и закрытия файла, для выбора рисунка или пиктограммы, для определения цвета библиотека .NET Framework предоставляет набор классов стандартных диалогов.Для использования стандартного диалога сначала требуется создать переменную типа данного диалога, а затем вызвать метод ShowDialog. Если в стандартном диалоге было выбрано значение (имя файла, цвет), то метод ShowDialog возвращает значение true.
Например:
// Вызов стандартного диалога Open OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.InitialDirectory = Environment.GetFolderPath( Environment.SpecialFolder.Personal); openFileDialog.Filter = "Text Files (*.txt)|*.txt| All Files (*.*)|*.*"; // Стандартные диалоги отображаются методом ShowDialog if (openFileDialog.ShowDialog(this) == DialogResult.OK) // Свойство FileName содержит имя выбранного файла {string FileName = openFileDialog.FileName; }
// Вызов стандартного диалога SaveAs SaveFileDialog saveFileDialog = new SaveFileDialog(); saveFileDialog.InitialDirectory = Environment.GetFolderPath( Environment.SpecialFolder.Personal); saveFileDialog.Filter = "Text Files (*.txt)|*.txt| All Files (*.*)|*.*"; if (saveFileDialog.ShowDialog(this) == DialogResult.OK) {string FileName = saveFileDialog.FileName;}
Работа с меню
Элемент управления MenuStrip (линейка меню) представляет собой контейнер для меню, размещаемый в форме. Объект ToolStripMenuItem может быть добавлен в MenuStrip. Объект ToolStripMenuItemthat является отдельным элементом меню, который может быть самостоятельной командой или родительским меню для других элементов подменю.MenuStrip служит контейнером для объектов следующих классов:
Класс MenuStrip заменяет и расширяет класс MainMenu предыдущих версий (класс Main-Menu оставлен для обратной совместимости и дальнейшего использования).
Свойство окна формы MainMenuStrip определяет линейку меню для данного окна.
Например:
// Создание объекта MenuStrip с новым окном подменю MenuStrip ms = new MenuStrip(); // Создание подменю ToolStripMenuItem windowMenu = new ToolStripMenuItem("Window"); ToolStripMenuItem windowNewMenu = new ToolStripMenuItem("New", null, // Обработчик данной команды: new EventHandler(windowNewMenu_Click)); // Список элементов меню windowMenu.DropDownItems.Add(windowNewMenu); ((ToolStripDropDownMenu) (windowMenu.DropDown)).ShowImageMargin = false; ((ToolStripDropDownMenu) (windowMenu.DropDown)).ShowCheckMargin = true;
// Указывает, что ToolStripMenuItem будет отображать // список дочерних форм ms.MdiWindowListItem = windowMenu;
// Добавление окна ToolStripMenuItem к линейке меню ms.Items.Add(windowMenu);
// Встраивание линейки меню в верх формы. ms.Dock = DockStyle.Top;
// Свойство Form.MainMenuStrip определяет линейку меню this.MainMenuStrip = ms;
Листинг 23.3.
Для добавления к форме линейки меню следует на панели инструментов выбрать элемент управления MenuStrip (или MainMenu в предыдущих версиях).

Редактор формы
Редактор формы позволяет в графическом режиме разместить на форме все требуемые элементы управления, определить первоначальное значение свойств этих элементов и создать для них обработчики событий.При размещении в форме нового элемента управления соответствующий код добавляется в файл формы. Аналогично для задания значения каждого свойства в файл формы добавляется соответствующий set-метод. Методы-обработчики событий также помещаются в файл формы.
Следующий код представляет файл формы для формы, в которую добавлено два элемента управления - поле ввода и командная кнопка:
namespace D1 { // Подключение используемых пространств имен using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; // ref class объявляет управляемый класс C++ public ref class Form1 : public System::Windows::Forms::Form { public: Form1(void){InitializeComponent();} protected: ~Form1(){if (components) {delete components;}} // Объявление элемента управления "кнопка": private: System::Windows::Forms::Button^ button1; // Объявление элемента управления // "поле ввода": private: System::Windows::Forms::TextBox^ textBox1; // Объявление контейнера private:System::ComponentModel::Container ^components;
#pragma region Windows Form Designer generated code // Формируется дизайнером формы void InitializeComponent(void) { // Создание объекта "кнопка": this->button1 = (gcnew System::Windows::Forms::Button()); // Создание объекта "поле ввода": this->textBox1 = (gcnew System::Windows::Forms::TextBox()); this->SuspendLayout(); // Временно // приостанавливает события компоновки до // вызова метода ResumeLayout или // Задание свойств для кнопки button1 this->button1->Location = System::Drawing::Point(427, 22); this->button1->Name = L"button1"; this->button1->Size = System::Drawing::Size(75, 23); this->button1->TabIndex = 0; this->button1->Text = L"button1"; this->button1->UseVisualStyleBackColor = true; // Определение обаботчика события Click для кнопки this->button1->Click += gcnew System::EventHandler( this, &Form1::button1_Click); // Задание свойств для поля ввода textBox1 this->textBox1->Location = System::Drawing::Point(80, 25); this->textBox1->Name = L"textBox1"; this->textBox1->Size = System::Drawing::Size(100, 20); this->textBox1->TabIndex = 1; // Задание свойств для формы Form1 this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(548, 266); ..// Добавление в форму элементов управления this->Controls->Add(this->textBox1); this->Controls->Add(this->button1); this->Name = L"Form1"; this->Text = L"Form1"; this->ResumeLayout(false); this->PerformLayout(); } #pragma endregion // Обработчик события для кнопки private: System::Void button1_Click( System::Object^ sender, System::EventArgs^ e) {this->textBox1->Text = L"123456"; } }; // Конец реализации класса } // Конец пространства имен D1
Листинг 23.1.
При создании проекта приложения для языка C# сразу создается диалоговая форма. Автоматически формируемое приложение включает в себя файлы, приведенные на рис. 23.1.

Рис. 23.1. Файлы проекта для C#
Файл Program.cs - это главный файл приложения, содержащий метод main. В этот файл по умолчанию вставляется следующий код:
using System; using System.Collections.Generic; using System.Windows.Forms;
namespace D_1 { static class Program { [STAThread] static void Main() { Application.EnableVisualStyles(); // Разрешает использовать для элементов управления визуальные стили. Метод должен быть вызван до добавления в форму элементов управления // Метод введен для .NET Framework version 2.0.: Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); // Создание формы } } }
Файл Forms.Designer.cs содержит код метода InitializeComponent, в котором к окну формы добавляются все элементы управления и происходит настройка их свойств.
Также в данный файл помещается код объявления элементов управления, размещаемых в форме:
namespace D_1 {partial class Form1 { private System.ComponentModel.IContainer components = null; ///
#region Windows Form Designer generated code
///
Листинг 23.2.
Файл Forms.cs содержит описание всех добавляемых пользователем методов обработки событий ( переход между окном кода и окном дизайнера для формы может быть выполнен вызовом команды контекстного меню View Code или View Designer).
Для окна формы с двумя элементами управления - командная кнопка и поле ввода, и методом обработки события Click файл Forms.cs может содержать следующий код:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace D_1 { public partial class Form1 : Form { public Form1() // Конструктор { InitializeComponent(); // Метод определен //в файле Forms.Designer.cs } private void button1_Click(object sender,EventArgs e) { this.textBox1.Text = "123456789"; } } }
Создание формы
Формы Windows реализуются набором классов из пространства имен Systems.Windows.Forms.Базовым классом для всех окон является класс Form.
Для создания приложения-диалога на языке С++ следует:
// D1.cpp : главный файл проекта. #include "stdafx.h" #include "Form1.h" // Файл формы using namespace D1; [STAThreadAttribute] int main(array
Оператор gcnew создает экземпляр управляемого типа. Результатом выполнения данной операции является дескриптор, указывающий на объект управляемого типа. Для объявления переменной типа дескриптора используется операция ^. Доступ к свойствам и методам объекта через дескриптор выполняется операцией ->.
Создание новой формы
Для создания и отображения новой формы следует вызвать конструктор формы и затем вызвать метод Show. При этом, если создаваемая форма является дочерней, то следует установить значение свойства MdiParent.Например:
// Создание нового экземпляра дочерней формы Form childForm = new Form(); // Делаем форму дочерней // для MDI-формы // до ее отображения childForm.MdiParent = this; childForm.Text = "Window " + childFormNumber++; childForm.Show(); // Отображаем дочернюю форму
Закрытие формы
Закрытие формы выполняется вызовом метода Close. Для того, чтобы закрыть все дочерние формы в MDI-приложении, следует использовать свойство MdiChildren, содержащее массив дочерних форм.Например:
private void CloseAllToolStripMenuItem_Click( object sender, EventArgs e) { foreach (Form childForm in MdiChildren) { childForm.Close(); } }
Класс Form предоставляет большой набор свойств, включая следующие
| AutoScale | true | Окно и элементы управления масштабируются автоматически в зависимости от размера шрифта |
| Border.Style | FormBorder. Style.Sizable | Граница окна позволяет масштабирование окна |
| ControlBox | true | Окно содержит кнопку системного меню и кнопки управления (в верхнем правом углу). Кнопки управления показываются, если свойства MaximizeBox и MinimizeBox установлены в true |
| StartPosition | Возможны следующие значения: Manual; CenterScreen; WindowsDefaultLocation; WindowsDefaultBounds; CenterParent | |
| WindowState | СВозможны следующие значения: Normal; Minimized; Maximized | |
| MainMenuStrip | Указывает на линейку меню | |
| IsMdiContainer | Определяет, допускаются ли дочерние окна | |
| IsMdiChild | Определяет, является ли форма дочерним окном | |
| MdiChildren | Массив форм, содержащий дочерние формы | |
| MdiParent | Ссылка на родительскую MDI-форму | |
| Controls | Коллекция дочерних элементов управления | |
| Text | Заголовок окна | |
| OwnerForms | Массив форм, принадлежащих данной форме | |
| Owner | Владелец формы |
Класс Form предоставляет большой набор методов, включая следующие:
Для формы возможны следующие события:
Дочерние окна в MDI-приложении могут быть упорядочены вызовом метода LayoutMdi.
Например:
// Упорядочивание по горизонтали this.LayoutMdi( MdiLayout.TileHorizontal ); // Упорядочивание по вертикали this.LayoutMdi( MdiLayout.TileVertical ); // Расположение каскадом this.LayoutMdi( MdiLayout.Cascade );
Введение в программирование
Иерархия классов
Классы библиотеки VCL используют механизм простого наследования: один класс может иметь только одного предка. Корнем иерархии классов является класс TObject. Любой класс VCL-библиотеки наследуется от класса TObject.На рис. 24.1 представлена корневая часть дерева иерархии классов VCL-библиотеки.

Рис. 24.1. Иерархия классов VCL-библиотеки
Класс TApplication
Класс TApplication инкапсулирует объект "Windows-приложение". Посредством этого класса определяется интерфейс между разработчиком и средой Windows.В каждом приложении Delphi всегда автоматически создается один объект Application как экземпляр класса приложения. Для большинства приложений этот объект является экземпляром класса TApplication.
Компонент TApplication не отображается в палитре компонентов и не имеет публикуемых свойств. Для того чтобы иметь возможность перехватывать события для приложения, используя среду разработки IDE, можно добавить в любую форму проекта компонент TApplicationEvents.
Класс предоставляет большой набор свойств, включая следующие:
Класс TComponent
TComponentявляется предком всех компонентов VCL-библиотеки.Все потомки данного класса могут быть расположены в палитре компонентов.
Класс TComponent позволяет определять родительский элемент управления и владельца компонента.
Родительским элементом управления называется тот, в который непосредственно помещен данный компонент.
Владельцем всех компонентов, расположенных в форме, является сама форма.
Владельцем всех форм является приложение.
Если компонент расположен не непосредственно в форме, а, например, в компоненте типа TPanel, то владелец и родительский элемент управления у него будут различны.
Класс предоставляет большой набор свойств, включая следующие:
Класс TControl
TControl - это базовый класс всех элементов управления (включая и окно формы). Эти компоненты могут быть видимы во время выполнения. Для них определены такие свойства, как позиция, курсор, всплывающая подсказка, методы для рисования или перемещения элемента управления, события для манипуляций с помощью мыши.Класс предоставляет большой набор свойств, включая следующие:
Подсказка отображается только, если значение свойства ShowHint установлено равным True.
Класс предоставляет большой набор методов, включая следующие:
Класс TForm
TForm является базовым классом для создания окна формы.По умолчанию каждая новая создаваемая форма реализуется как потомок класса TForm. Форма может быть:
Класс предоставляет большой набор свойств, включая следующие:
Например:
if ActiveControl <> nil then ActiveControl.Left := ActiveControl.Left + 1; end;
Например:
{Закрытие всех дочерних MDI-форм} var Index1: Integer; begin with MyForm1 do for I := MDIChildCount-1 downto 0 do MDIChildren[Index1].Close; end;
Класс формы предоставляет большой набор методов, включая следующие:
Например:
{Обработчик события для команды меню Windows | Tile} procedure TForm1.TileFormsClick(Sender: TObject); begin this.TileMode := tbVertical; this.Tile; end;
Класс формы является контейнером для всех компонентов, размещаемых на форме. Для доступа к свойствам формы или именам компонентов можно использовать ключевое слово this. Если перед именем свойства отсутствует какой-либо идентификатор, то по умолчанию предполагается, что это свойство формы.
Класс TMainMenu
Класс TMainMenu инкапсулирует поведение линейки меню (menu bar) и соответствующих ниспадающих меню (drop-down menus) для окна формы.Этот класс определяет свойства и методы, позволяющие соединять ниспадающие меню главного меню с главными меню других форм и помогающие взаимодействовать с меню для OLE-объектов.
Процесс создания меню формы очень прост. Он состоит из трех этапов:
Если требуется синхронизировать код, выполняемый для пункта меню, с кодом, выполняемым для кнопки на панели управления, то создается объект "действие типа Taction", который указывается и для пункта меню, и для кнопки.
Класс предоставляет набор свойств, включая следующие:
Класс TMenuItem
Класс TMenuItem реализует поведение пунктов меню. Контейнером для объектов типа TMenuItem может быть компонент типа TMainMenu (линейка главного меню) или компонент типа TPopupMenu (контекстное меню).При создании меню на этапе проектирования редактор меню Menu Designer автоматически создает объекты типа TMenuItem для каждой команды меню.
Класс предоставляет набор свойств, включая следующие:
По умолчанию все пункты линейки меню имеют одинаковое значение GroupIndex. При использовании этого свойства для слияния меню следует установить значения каждого пункта линейки меню равным предыдущему или большим.
Если значение свойстваGroupIndex - добавляемого меню совпадает со значением свойства GroupIndex - пункта главного меню, то последний заменяется на добавляемый. Если несколько пунктов меню главного окна имеет одинаковое значение свойства GroupIndex -, то и заменены они могут быть только несколькими пунктами добавляемого меню: первый пункт с одинаковым значением заменяется на первый добавляемый пункт с одинаковым значением, второй - на второй и т.д.
Если значение свойства GroupIndex добавляемого пункта линейки меню лежит между значениями свойства GroupIndex пунктов линейки меню главного окна, то добавляемый пункт вставляется между ними.
Класс предоставляет набор методов, включая следующие:
Например:
var NewItem: TMenuItem; i : integer; begin // Создание элемента "разделительная линия": NewItem := TMenuItem.Create(Self); NewItem.Caption := '-'; // Заголовок пункта меню // Добавление пункта меню к меню Windows: Windows.Add(NewItem); // Cоздадим и добавим пункт меню для каждой формы for i := 0 to Screen.FormCount-1 do begin NewItem := TMenuItem.Create(Self); NewItem.Caption := Screen.Forms[i].Name; Windows.Add(NewItem); end; end;
Этот метод может быть переопределен разработчиком для того, чтобы запрограммировать собственный ответ на выбор пользователем пункта меню.
Разработчик может создать один общий объект "действие" и для пункта меню, и для кнопки панели инструментов. Для определения действия на этапе проектирования следует:
Класс TObject
Класс TObject инкапсулирует общие черты поведения всех объектов VCL-библиотеки. Если при создании нового объекта не указан базовый класс, то Delphi автоматически использует как предка класс TObject.Объявление нового класса выполняется в секции type. Если после слова class в скобках не указано никакого наследуемого класса, то по умолчанию предполагается, что создаваемый класс наследуем от класса TObject.
Например:
type TMyClass = class // Эти два объявления type TMyClass = class(TObject) //являются эквивалентными
Класс TPopupMenu
Класс TPopupMenu инкапсулирует поведение контекстных меню, также называемых всплывающими или popup-меню.Он предназначен для создания меню, отображаемых при щелчке пользователя правой кнопкой мыши на элементе управления. Чтобы контекстное меню поставить в соответствие конкретному элементу управления, следует установить значение свойства PopupMenu элемента управления равным имени объекта TPopupMenu. Для этого в инспекторе объектов следует выбрать значение свойства PopupMenu из автоматически предлагаемого списка объектов типа TPopupMenu.
Базовым классом для TPopupMenu является класс TMenu.
Следующий пример иллюстрирует отображение контекстного меню в указанной точке экрана по щелчку мыши:
procedure Form1.FormCreate(Sender: TObject); begin PopupMenu1.AutoPopup := False; end; procedure Form1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin PopupMenu1.Popup(X, Y); end;

Класс TScreen
Каждое приложение Delphi имеет глобальную переменную Screen типа TScreen. Эта переменная определена как var Screen: TScreen;.Компонент TScreen, так же как и компонент TApplication, недоступен из инспектора объектов. Этот компонент предназначен для обеспечения доступа к устройству вывода - экрану. Его свойства содержат информацию об используемом разрешении монитора, курсорах и шрифтах, доступных для приложения, списке форм приложения и активной форме.
Класс TWinControl
Класс TWinControl является базовым классом всех оконных элементов управления.Класс предоставляет большой набор свойств, включая следующие:
Компоненты
Компонент Delphi - это особый вид объектов - визуальный объект (визуальный для проектирования, а не для отображения пользователя). Создавать и редактировать такой объект можно как программным путем, так и на этапе проектирования.При выполнении программы компоненты делятся на визуальные, которые видит пользователь, и невизуальные, для которых нет возможности их отображения, но доступ к свойствам которых разрешен.
Все компоненты имеют общего предка - класс TComponent .
Delphi предоставляет широкий набор компонентов, называемый иногда VCL-библиотекой. Все компоненты Delphi могут быть доступны через палитру компонентов.
В настоящее время в Borland Developer Studio входит Delphi 2006 for Win32 (использует библиотеку VCL) и Delphi 2006 for Microsoft .NET (использует библиотеку Framework 1.1).
Часть компонентов являются элементами управления. В основном это элементы управления Windows. Доступ к элементам управления возможен не только на этапе проектирования, но и во время выполнения приложения.
Элементы управления можно подразделить на оконные и неоконные. Оконные элементы могут получать фокус и имеют дескриптор окна. Предком всех оконных элементов управления является абстрактный класс TWinControl. Предком неоконных элементов управления является абстрактный класс TGraphicControl.
При добавлении в форму любого компонента из палитры компонентов Delphi автоматически формирует программный код для создания объекта (переменной) данного типа. Переменная добавляется как член класса данной формы.
Объекты
Объект Delphi представляет собой набор свойств и методов, включающих также обработчики событий. Свойства, называемые иногда атрибутами, являются данными, содержащимися в объекте. Методы описывают действия, реализованные для данного объекта.Все объекты имеют общего предка - класс TObject.
Введение в программирование
Компонент главного меню} File1: TMenuItem;
|
type TMainForm = class(TForm) MainMenu1: TMainMenu; { Компонент главного меню} File1: TMenuItem; {Компоненты элементов меню} FileNewItem: TMenuItem; FileOpenItem: TMenuItem; FileCloseItem: TMenuItem; Window1: TMenuItem; Help1: TMenuItem; N1: TMenuItem; FileExitItem: TMenuItem; WindowCascadeItem: TMenuItem; WindowTileItem: TMenuItem; WindowArrangeItem: TMenuItem; HelpAboutItem: TMenuItem; OpenDialog: TOpenDialog; FileSaveItem: TMenuItem; FileSaveAsItem: TMenuItem; Edit1: TMenuItem; CutItem: TMenuItem; CopyItem: TMenuItem; PasteItem: TMenuItem; WindowMinimizeItem: TMenuItem; StatusBar: TStatusBar; {Компонент строки состояния} ActionList1: TActionList; {Компонент для определения списка именованных действий} EditCut1: TEditCut; {Компонент для копирования в буфер обмена} EditCopy1: TEditCopy; EditPaste1: TEditPaste; FileNew1: TAction; {Компонент именованного действия} FileSave1: TAction; FileExit1: TAction; FileOpen1: TAction; FileSaveAs1: TAction; WindowCascade1: TWindowCascade; WindowTileHorizontal1: TWindowTileHorizontal; WindowArrangeAll1: TWindowArrange; WindowMinimizeAll1: TWindowMinimizeAll; HelpAbout1: TAction; FileClose1: TWindowClose; WindowTileVertical1: TWindowTileVertical; WindowTileItem2: TMenuItem; ToolBar2: TToolBar; {Панель инструментов} ToolButton1: TToolButton; {Кнопки панели инструментов} ToolButton2: TToolButton; ToolButton3: TToolButton; ToolButton4: TToolButton; ToolButton5: TToolButton; ToolButton6: TToolButton; ToolButton9: TToolButton; ToolButton7: TToolButton; ToolButton8: TToolButton; ToolButton10: TToolButton; ToolButton11: TToolButton; ImageList1: TImageList; procedure FileNew1Execute(Sender: TObject); procedure FileOpen1Execute(Sender: TObject); procedure HelpAbout1Execute(Sender: TObject); procedure FileExit1Execute(Sender: TObject); end; |
| Листинг 25.1. |
| Закрыть окно |
type
TMainForm = class(TForm)
MainMenu1: TMainMenu; {Компонент главного меню}
File1: TMenuItem; {Компоненты элементов меню}
FileNewItem: TMenuItem;
FileOpenItem: TMenuItem;
FileCloseItem: TMenuItem;
Window1: TMenuItem;
Help1: TMenuItem;
N1: TMenuItem;
FileExitItem: TMenuItem;
WindowCascadeItem: TMenuItem;
WindowTileItem: TMenuItem;
WindowArrangeItem: TMenuItem;
HelpAboutItem: TMenuItem;
OpenDialog: TOpenDialog;
FileSaveItem: TMenuItem;
FileSaveAsItem: TMenuItem;
Edit1: TMenuItem;
CutItem: TMenuItem;
CopyItem: TMenuItem;
PasteItem: TMenuItem;
WindowMinimizeItem: TMenuItem;
StatusBar: TStatusBar; {Компонент строки состояния}
ActionList1: TActionList; { Компонент для определения
списка именованных действий}
EditCut1: TEditCut; {Компонент для копирования
в буфер обмена}
EditCopy1: TEditCopy;
EditPaste1: TEditPaste;
FileNew1: TAction; {Компонент именованного действия}
FileSave1: TAction; FileExit1: TAction;
FileOpen1: TAction; FileSaveAs1: TAction;
WindowCascade1: TWindowCascade;
WindowTileHorizontal1: TWindowTileHorizontal;
WindowArrangeAll1: TWindowArrange;
WindowMinimizeAll1: TWindowMinimizeAll;
HelpAbout1: TAction;
FileClose1: TWindowClose;
WindowTileVertical1: TWindowTileVertical;
WindowTileItem2: TMenuItem;
ToolBar2: TToolBar; {Панель инструментов}
ToolButton1: TToolButton; {Кнопки панели инструментов}
ToolButton2: TToolButton;
ToolButton3: TToolButton;
ToolButton4: TToolButton;
ToolButton5: TToolButton;
ToolButton6: TToolButton;
ToolButton9: TToolButton;
ToolButton7: TToolButton;
ToolButton8: TToolButton;
ToolButton10: TToolButton;
ToolButton11: TToolButton;
ImageList1: TImageList;
procedure FileNew1Execute(Sender: TObject);
procedure FileOpen1Execute(Sender: TObject);
procedure HelpAbout1Execute(Sender: TObject);
procedure FileExit1Execute(Sender: TObject);
end;
Заголовком окна будет имя файла}
|
{Создать новый документ} procedure TMainForm.FileNew1Execute(Sender: TObject); begin {Создание дочернего окна с новым именем} CreateMDIChild('NONAME' + IntToStr(MDIChildCount + 1)); end; {Открыть документ} procedure TMainForm.FileOpen1Execute(Sender: TObject); begin if OpenDialog.Execute then {Выбор имени документа} CreateMDIChild(OpenDialog.FileName); {Создание окна} end; procedure TMainForm.CreateMDIChild(const Name: string); var Child: TMDIChild; begin { Класс TMDIChild определен в модуле CHILDWIN.PAS } Child := TMDIChild.Create(Application); {Создание окна} Child.Caption := Name; { Заголовком окна будет имя файла} if FileExists(Name) then // Дочернее окно содержит поле типа TMemo, в которое // выполняется загрузка текстового файла: Child.Memo1.Lines.LoadFromFile(Name); end; Для завершения приложения выполняется следующая процедура: procedure TMainForm.FileExit1Execute(Sender: TObject); begin Close; end; В модуле childwin.pas находится код дочерней формы: type TMDIChild = class(TForm) Memo1: TMemo; {Поле для отображения текста} procedure FormClose(Sender: TObject; var Action: TCloseAction); end; implementation procedure TMDIChild.FormClose(Sender: TObject; var Action: TCloseAction); begin Action := caFree; end; end. |
| Листинг 25.2. |
| Закрыть окно |
{Создать новый документ}
procedure TMainForm.FileNew1Execute(Sender: TObject);
begin
{Создание дочернего окна с новым именем}
CreateMDIChild('NONAME' + IntToStr(MDIChildCount + 1));
end;
{Открыть документ}
procedure TMainForm.FileOpen1Execute(Sender: TObject);
begin
if OpenDialog.Execute then {Выбор имени документа}
CreateMDIChild(OpenDialog.FileName); {Создание окна}
end;
procedure TMainForm.CreateMDIChild(const Name: string);
var
Child: TMDIChild;
begin
{ Класс TMDIChild определен в модуле CHILDWIN.PAS }
Child := TMDIChild.Create(Application); {Создание окна}
Child.Caption := Name; {Заголовком окна будет
имя файла}
if FileExists(Name) then
// Дочернее окно содержит поле типа TMemo, в которое
// выполняется загрузка текстового файла:
Child.Memo1.Lines.LoadFromFile(Name);
end;
Для завершения приложения выполняется следующая процедура:
procedure TMainForm.FileExit1Execute(Sender: TObject);
begin Close; end;
В модуле childwin.pas находится код дочерней формы:
type
TMDIChild = class(TForm)
Memo1: TMemo; {Поле для отображения текста}
procedure FormClose(Sender: TObject;
var Action: TCloseAction);
end;
implementation
procedure TMDIChild.FormClose(Sender: TObject;
var Action: TCloseAction);
begin Action := caFree; end;
end.
Window1 object StatusBar: TStatusBar AutoHint
|
object MainForm: TMainForm Caption = 'Приложение с MDI-интерфейсом' FormStyle = fsMDIForm Menu = MainMenu1 WindowMenu = Window1 object StatusBar: TStatusBar AutoHint = True Panels = <> SimplePanel = True end object ToolBar2: TToolBar Images = ImageList1 object ToolButton9: TToolButton Action = FileNew1 {Именованное действие, которое будет выполнено при щелчке на кнопке} end object ToolButton1: TToolButton Action = FileOpen1 end {Кнопки панели инструментов} ... end object MainMenu1: TMainMenu Images = ImageList1 object File1: TMenuItem Caption = '&File' object FileNewItem: TMenuItem Action = FileNew1 end object FileOpenItem: TMenuItem Action = FileOpen1 end object FileCloseItem: TMenuItem Action = FileClose1 end ... object FileExitItem: TMenuItem Action = FileExit1 end end object Edit1: TMenuItem ... end object Window1: TMenuItem ... end end object OpenDialog: TOpenDialog {Диалог Open} Filter = 'All files (*.*)|*.*' end object ActionList1: TActionList object FileNew1: TAction Category = 'File' Caption = '& New' OnExecute = FileNew1Execute end object FileOpen1: TAction Category = 'File' Caption = '&Open' OnExecute = FileOpen1Execute end ... end end |
| Листинг 25.3. |
| Закрыть окно |
object MainForm: TMainForm
Caption = 'Приложение с MDI-интерфейсом'
FormStyle = fsMDIForm
Menu = MainMenu1
WindowMenu = Window1
object StatusBar: TStatusBar
AutoHint = True
Panels = <>
SimplePanel = True
end
object ToolBar2: TToolBar
Images = ImageList1
object ToolButton9: TToolButton
Action = FileNew1 {Именованное действие,
которое будет выполнено при щелчке на кнопке}
end
object ToolButton1: TToolButton
Action = FileOpen1
end
{Кнопки панели инструментов}
...
end
object MainMenu1: TMainMenu
Images = ImageList1
object File1: TMenuItem
Caption = '&File'
object FileNewItem: TMenuItem
Action = FileNew1
end
object FileOpenItem: TMenuItem
Action = FileOpen1
end
object FileCloseItem: TMenuItem
Action = FileClose1
end
...
object FileExitItem: TMenuItem
Action = FileExit1
end
end
object Edit1: TMenuItem
...
end
object Window1: TMenuItem
...
end
end
object OpenDialog: TOpenDialog {Диалог Open}
Filter = 'All files (*.*)|*.*'
end
object ActionList1: TActionList
object FileNew1: TAction
Category = 'File'
Caption = '&
New'
OnExecute = FileNew1Execute
end
object FileOpen1: TAction
Category = 'File'
Caption = '&Open'
OnExecute = FileOpen1Execute
end
...
end
end
Главная форма приложения
По умолчанию при добавлении в проект новой формы для нее в главном файле приложения автоматически формируется строка кода для создания формы.Например:
Application.CreateForm (TForm1, Form1);.
Для добавления в проект новой формы достаточно выполнить команду меню File|New Form.
Окно свойств проекта позволяет определить:
Если приложение содержит много форм, то для автоматического создания всех форм может не хватить памяти. Более рационально создавать формы по мере необходимости.
Для создания формы используется метод Create.
Созданная форма может быть отображена:
Использование модальной формы
Выполнение модальной формы завершается сразу после вызова метода ShowModal, поэтому память из под нее следует освобождать в вызывающей форме вызовом метода Free.Например:
Form2:=TForm2.Create(self) ; Form2.ShowModal; Form2.Free;
Результат выполнения модальной формы устанавливается свойством ModalResult командных кнопок. Для того чтобы проверить код ответа (какая кнопка была нажата), следует сравнить значение, возвращаемое методом ShowModal.
Например:
if Form2.ShowModal = mrOk then // Нажата кнопка Ок else Abort; // Завершение приложения
Для закрытия модального диалога не следует вызывать метод Close, достаточно использовать командные кнопки с установленным значением свойства ModalResult.
Использование немодальной формы
Время жизни обычного немодального диалога неопределенно и не завершается после вызова метода Show. Поэтому обычный диалог следует завершать в обработчике события OnClose.Для вызова этого обработчика события можно вызвать метод Close или выполнить щелчок мышью по соответствующей кнопке в строке заголовка окна.
Если перед запуском диалога он был создан (не является автоматически создаваемым), то после его закрытия следует освободить занимаемую им память. Это выполняется в обработчике события OnClose вызовом метода Release.
Классы стандартных диалогов
Страница Dialogs палитры компонентов содержит ряд компонентов, предназначенных для реализации различных стандартных диалогов. Это диалоги для открытия файла, сохранения файла, выбора цвета или шрифта, открытия или сохранения графического файла.Стандартный диалог открывается при вызове метода Execute. При выборе пользователем значения или щелчке пользователя на кнопке OK возвращается значение True. Если пользователь нажал кнопку Cancel или клавишу Escape, то выполнение стандартного диалога возвращает значение False. Для настройки появления и поведения диалога используется свойство Options. Чтобы закрыть диалог программно, следует вызвать метод CloseDialog.
В следующей таблице приведено описание стандартных диалогов, доступных посредством страницы Dialogs палитры компонентов.
| TOpenDialog | Отображает стандартный диалог Open(имя выбранного файла указываетсяв свойстве FileName) |
| TSaveDialog | Отображает стандартный диалог Save |
| TOpenPictureDialog | Отображает стандартный модальный диалог для выбора и открытия графических файлов. Этот диалог идентичен диалогу Open, но имеет дополнительно панель для просмотра изображения |
| TSavePictureDialog | Отображает стандартный диалог для сохранения графических файлов. Этот диалог имеет панель для просмотра изображения |
| TFontDialog | Выводит стандартный диалог Font, используемый для определения шрифта, размера и стиля отображения данных |
| СTColorDialog | Отображает стандартный диалог Color (выбранный цвет указывается в свойстве Color) |
| TPrintDialog | Отображает стандартный диалог Print |
| TPrinterSetupDialog | Отображает стандартный диалог Printer Setup |
| TFindDialog | Отображает стандартный диалог Find |
| TReplaceDialog | Отображает стандартный диалог Replace |
Следующий пример иллюстрирует использование стандартного диалога для выбора имени открываемого файла:
var F: TextFile; S: string; begin if OpenDialog1.Execute then // Показать диалог Open begin AssignFile(F, OpenDialog1.FileName); // Назначить // файловой переменной имя выбранного файла Reset(F); Readln(F, S); //Прочитать первую строку файла Edit1.Text := S; // Поместить строку в компонент TEdit CloseFile(F); // Закрыть файл end; end;

Проекты
Любое приложение в среде проектирования Delphi создается как некоторый проект. В зависимости от типа приложения, в проект входит различный набор файлов проекта. Но в любом случае составной частью проекта является файл проекта с расширением DPR. Каждая форма в проекте представляется двумя файлами: файл модуля с расширением PAS и файл описания формы с расширением DFM. Файл описания формы не может редактироваться непосредственно, данные в него заносятся средой проектирования. В каждый проект входит один главный файл приложения. Для диалоговых приложений этот файл начинается с ключевого слова program. Файл модуля для формы начинается ключевым словом unit.Для создания нового приложения среда проектирования Delphi предоставляет большой набор шаблонов приложений, включающий приложение-диалог, SDI-приложение и MDI-приложение.
Основное отличие автоматически формируемого макета приложения на базе шаблона MDI-приложения от применения шаблона SDI-приложения заключается в поддержке одновременной работы с несколькими документами. Каждый новый документ открывается в создаваемом дочернем окне.
Дополнительно шаблон MDI-приложения содержит кнопки для изменения расположения окон, а в меню Windows добавляются имена всех открытых документов.
Создание DLL-библиотеки
DLL-библиотека позволяет объединить в единое целое повторно используемый код. Функции из DLL-библиотеки могут подключаться динамически во время выполнения, в отличие от функций из пакетов Delphi линкуемых статически на этапе компиляции приложения.Для того чтобы создать DLL-библиотеку, следует использовать шаблон приложения DLL Wizard.
В отличие от обычного модуля, начинающегося с ключевого слова unit, модуль DLL-библиотеки начинается с ключевого слова library.
Секция uses модуля DLL-библиотеки требует подключения только двух пакетов: SysUtils и Classes.
Функции из DLL-библиотеки могут вызываться как из приложений, разработанных в Delphi, так и из приложений, написанных на других языках программирования, таких как C++.
Порядок выделения памяти под параметры и освобождения ее различен для разных языков программирования. Чтобы не возникла ошибка времени выполнения, объявление функции в DLL-библиотеке и ее объявление в приложении должны использовать одинаковый механизм передачи параметров.
При объявлении процедуры или функции может быть указан один из следующих механизмов передачи параметров:
Способ передачи параметров указывается через точку с запятой после описания функции.
Например: function F1(X, Y, Z: Real): Real; stdcall;
Различные способы передачи параметров определяют порядок их передачи (слева направо или справа налево), а также указывают, кто будет освобождать память стека (вызываемая или вызывающая процедура).
Для того чтобы функцию, описанную в DLL-библиотеке, можно было вызвать из другого приложения, эту функцию следует экспортировать. Список всех экспортируемых функций указывается в секции exports через запятую и завершается символом "точка с запятой".
Экспорт функций может выполняться тремя способами:
Например:
library Project1; uses SysUtils, Classes; {$R *.res} function F1(X: Integer): Integer; stdcall; begin F1:=X*2; end; function F2(X: Integer): Integer; stdcall; begin F2:=X*X; end; exports F1 , {Функция будут доступна по имени F1} F2 index 2 ; {Функция будут доступна по индексу 2} end.
DLL-библиотека не является выполняемым модулем, поэтому для получения dll-файла достаточно произвести компиляцию проекта.
Создание MDI-приложения
При использовании шаблона MDI-приложения создается проект, состоящий из трех модулей (main.pas, childwin.pas, about.pas) и главного файла приложения.Для главной формы приложения (main.pas) свойство FormStyle будет установлено равным fsMDIForm, а для дочерней формы (childwin.pas) - fsMDIChild.
Класс главной формы приложения определяется следующим кодом:
type TMainForm = class(TForm) MainMenu1: TMainMenu; {Компонент главного меню} File1: TMenuItem; {Компоненты элементов меню} FileNewItem: TMenuItem; FileOpenItem: TMenuItem; FileCloseItem: TMenuItem; Window1: TMenuItem; Help1: TMenuItem; N1: TMenuItem; FileExitItem: TMenuItem; WindowCascadeItem: TMenuItem; WindowTileItem: TMenuItem; WindowArrangeItem: TMenuItem; HelpAboutItem: TMenuItem; OpenDialog: TOpenDialog; FileSaveItem: TMenuItem; FileSaveAsItem: TMenuItem; Edit1: TMenuItem; CutItem: TMenuItem; CopyItem: TMenuItem; PasteItem: TMenuItem; WindowMinimizeItem: TMenuItem; StatusBar: TStatusBar; {Компонент строки состояния} ActionList1: TActionList; {Компонент для определения списка именованных действий} EditCut1: TEditCut; {Компонент для копирования в буфер обмена} EditCopy1: TEditCopy; EditPaste1: TEditPaste; FileNew1: TAction; {Компонент именованного действия} FileSave1: TAction; FileExit1: TAction; FileOpen1: TAction; FileSaveAs1: TAction; WindowCascade1: TWindowCascade; WindowTileHorizontal1: TWindowTileHorizontal; WindowArrangeAll1: TWindowArrange; WindowMinimizeAll1: TWindowMinimizeAll; HelpAbout1: TAction; FileClose1: TWindowClose; WindowTileVertical1: TWindowTileVertical; WindowTileItem2: TMenuItem; ToolBar2: TToolBar; {Панель инструментов} ToolButton1: TToolButton; {Кнопки панели инструментов} ToolButton2: TToolButton; ToolButton3: TToolButton; ToolButton4: TToolButton; ToolButton5: TToolButton; ToolButton6: TToolButton; ToolButton9: TToolButton; ToolButton7: TToolButton; ToolButton8: TToolButton; ToolButton10: TToolButton; ToolButton11: TToolButton; ImageList1: TImageList; procedure FileNew1Execute(Sender: TObject); procedure FileOpen1Execute(Sender: TObject); procedure HelpAbout1Execute(Sender: TObject); procedure FileExit1Execute(Sender: TObject); end;
Листинг 25.1.
В коде модуля main. pas должно быть указано использование двух других модулей приложения: uses ChildWin, About;.
Процедура создания нового документа или открытия существующего реализована следующим кодом:
{Создать новый документ} procedure TMainForm.FileNew1Execute(Sender: TObject); begin {Создание дочернего окна с новым именем} CreateMDIChild('NONAME' + IntToStr(MDIChildCount + 1)); end; {Открыть документ} procedure TMainForm.FileOpen1Execute(Sender: TObject); begin if OpenDialog.Execute then {Выбор имени документа} CreateMDIChild(OpenDialog.FileName); {Создание окна} end; procedure TMainForm.CreateMDIChild(const Name: string); var Child: TMDIChild; begin { Класс TMDIChild определен в модуле CHILDWIN.PAS } Child := TMDIChild.Create(Application); {Создание окна} Child.Caption := Name; {Заголовком окна будет имя файла} if FileExists(Name) then // Дочернее окно содержит поле типа TMemo, в которое // выполняется загрузка текстового файла: Child.Memo1.Lines.LoadFromFile(Name); end; Для завершения приложения выполняется следующая процедура: procedure TMainForm.FileExit1Execute(Sender: TObject); begin Close; end; В модуле childwin.pas находится код дочерней формы: type TMDIChild = class(TForm) Memo1: TMemo; {Поле для отображения текста} procedure FormClose(Sender: TObject; var Action: TCloseAction); end; implementation procedure TMDIChild.FormClose(Sender: TObject; var Action: TCloseAction); begin Action := caFree; end; end.
Листинг 25.2.
Главная форма приложения описывается следующими свойствами (полужирным шрифтом и курсивом выделены свойства, значения которых не могут быть другими):
object MainForm: TMainForm Caption = 'Приложение с MDI-интерфейсом' FormStyle = fsMDIForm Menu = MainMenu1 WindowMenu = Window1 object StatusBar: TStatusBar AutoHint = True Panels = <> SimplePanel = True end object ToolBar2: TToolBar Images = ImageList1 object ToolButton9: TToolButton Action = FileNew1 {Именованное действие, которое будет выполнено при щелчке на кнопке} end object ToolButton1: TToolButton Action = FileOpen1 end {Кнопки панели инструментов} ...
end object MainMenu1: TMainMenu Images = ImageList1 object File1: TMenuItem Caption = '&File' object FileNewItem: TMenuItem Action = FileNew1 end object FileOpenItem: TMenuItem Action = FileOpen1 end object FileCloseItem: TMenuItem Action = FileClose1 end ... object FileExitItem: TMenuItem Action = FileExit1 end end object Edit1: TMenuItem ... end object Window1: TMenuItem ... end end object OpenDialog: TOpenDialog {Диалог Open} Filter = 'All files (*.*)|*.*' end object ActionList1: TActionList object FileNew1: TAction Category = 'File' Caption = '& New' OnExecute = FileNew1Execute end object FileOpen1: TAction Category = 'File' Caption = '&Open' OnExecute = FileOpen1Execute end ... end end
Листинг 25.3.
Дочерняя форма фактически является обычной формой, в которую помещен один объект типа TMemo (многострочное поле редактирования). Она описывается следующими свойствами:
object MDIChild: TMDIChild FormStyle = fsMDIChild Position = poDefault Visible = True OnClose = FormClose object Memo1: TMemo WordWrap = False end end
После того как макет приложения создан на базе шаблона MDI-приложения, его можно изменять, добавляя новые команды меню и компоненты типа TAction для реализации именованных действий. В проект можно добавлять новые формы и редактировать уже существующие.
Статическое и динамическое подключения DLL-библиотеки
DLL-библиотека может подключаться или статически, или динамически. При подключении DLL-библиотеки она загружается в память приложения.При статическом подключении DLL-библиотека загружается один раз при запуске приложения.
На всем протяжении выполнения приложения имя функции, импортируемой из DLL-библиотеки, которая была подключена статически, указывает на одну и ту же функцию (точку входа в DLL) в одной и той же DLL.
В отличие от статического подключения DLL-библиотеки, выполняемого в момент загрузки приложения, динамическое подключение может быть выполнено в любой точке выполнения программы. После вызова функции из DLL-библиотеки ее можно отключить. При одновременном использовании нескольких DLL-библиотек это дает ощутимую экономию памяти.
Для динамического подключения DLL-библиотеки применяются функции Windows API. Windows API - это набор стандартных функций, используемый для реализации взаимодействия с операционной системой.
Все функции из статически подключаемой DLL-библиотеки, которые будут использоваться в приложении, первоначально должны быть объявлены как внешние.
При этом следует указать, если требуется, модификатор вызова. Если функция вызывается по индексу, то для нее следует задать имя, используемое в приложении и индекс функции в DLL-библиотеке.
Объявления внешних функций выполняется в секции implementation до использования этих функций.
Объявление внешней функции с ключевым словом external определяет, что будет применено статическое связывание.
Например:
function F1 (i: Integer): Integer; stdcall; external 'MyLib.dll';
При вызове функции из динамически подключаемой DLL-библиотеки, вместо определения имени функции как external, в случае статического связывания, следует определить новый тип, соответствующий типу вызываемой функции, и создать переменную данного типа.
Определение типа функции или процедуры описывается так:
Новый_тип =function(список_параметров):тип_функции; модификатор_доступа; или
Новый_тип=procedure(список_параметров); модификатор доступа;.
Следующий пример иллюстрирует вызов функции из динамически подключаемой DLL-библиотеки:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button3: TButton; procedure Button3Click(Sender: TObject); end; TMyF1=function( i:Integer):Integer; stdcall; {Создание типа функции} var Form1: TForm1; MyF1 : TMyF1; {Объявление переменной типа функции} implementation {$R *.dfm} procedure TForm1.Button3Click(Sender: TObject); var h: Integer; begin h:=LoadLibrary('MyLib.dll'); // Динамическое подключение if h <> 0 then begin @MyF1:=GetProcAddress(h,'F1'); // Получение адреса функции if @MyF1 <> nil then ShowMessage(IntToStr(MyF1(20)); // Вызов Функции FreeLibrary(h); // Освобождение dll-библиотеки end; end; end.
Введение в программирование
Блоки прослушивания пакета JDK
Пакет java.util содержит интерфейс EventListener, который наследуется всеми блоками прослушивания.Для различных классов компонентов предназначаются разные интерфейсы блоков прослушивания.
В следующей таблице приведены некоторые интерфейсы блоков прослушивания, наследуемые от интерфейса EventListener.
| Action | javax.swing | Расширяет интерфейс ActionListener, позволяя нескольким компонентам использовать одни и те же обработчики событий |
| ActionListener | java.awt.event | Этот блок прослушивания регистрируется методом addActionListener. При возникно- вении события "действие" вызывается метод actionPerformed(ActionEvent e) компонента, зарегистрировавшего данный блок прослушивания. Интерфейс исполь- зуется для обработки событий меню, кнопок и т.п. |
| AdjustmentListener | java.awt.event | Используется для получения регулируемых событий |
| AncestorListener | javax.swing.event | Интерфейс поддерживает уведомления при изменении компонента класса JComponent или одного из его предков. Это касается перемещения компонента, перехода из видимого состояния в неви димое или обратно, выполнения метода setVisible(), а также при добавлении и удалении компонентов из иерархии |
| CaretListener | javax.swing.event | Используется при изменении позиции ввода в текстовых компонентах. Этот интерфейс реализован классом javax.swing.text.JTextComponent.AccessibleJTextComponent |
| CaretListener | javax.swing.event | Используется при изменении позиции ввода в текстовых компонентах. Этот интерфейс реализован классом javax.swing.text.JTextComponent.AccessibleJTextComponent |
| CellEditorListener | javax.swing.event | Используется для отслеживания изме нений в редактируемой ячейке (CellEditor), касающихся завершения или отмены редактирования. Этот интерфейс реализован следующими классами: JTable, JTable.AccessibleJTable, BasicTreeUI.CellEditorHandler |
| ChangeListener | javax.swing.event | Определяет объект, выполняющий прослушивание событий ChangeEvent. Интерфейс объявляет всего один метод: void stateChanged(ChangeEvent e). Этот интерфейс реализован несколькими классами, включая: JMenuItem.AccessibleJMenuItem, BasicButtonListener, JTabbedPane.AccessibleJTabbedPane. |
| ContainerListener | java.awt.event | Интерфейс объявляет два метода: public void componentAdded(ContainerEvent e) и public void componentRemoved(ContainerEvent e), вызываемые при добавлении компонента в контейнер или при удалении из контейнера. Для этого интерфейса реализован класс-адаптер ContainerAdapter |
| DocumentListener | javax.swing.event | Интерфейс используется при изменении текстового документа |
| DragGestureListener | java.awt.dnd | Интерфейс используется при инициа- лизации процесса перетаскивания объекта |
| DragSourceListener | java.awt.dnd | Интерфейс используется для реализации механизма перетаскивания и сброса- объектов (Drag & Drop). В этот интер- фейсе объявлены методы для отслежи- вания действий пользователя по пере- мещению объекта. Этот интерфейс реализован классом DragSourceContext |
| DropTargetListener | java.awt.dnd | Интерфейс может быть использован для обработки событий, инициируемых в то время, когда объект находится над местом сброса |
| FocusListener | java.awt.event | Интерфейс используется для обработки событий получения или потери фокуса компонентом. Блок прослушивания регистрируется методом addFocusListener. Для этого интерфейса реализован класс адаптер FocusAdapter |
| ItemListener | java.awt.event | Интерфейс используется для обработки события выделения элемента и объяв- ляет только один метод public void itemStateChanged(ItemEvent e). Блок прослушивания регистрируется методом addItemListener |
| KeyListener | java.awt.event | Интерфейс используется для обработки событий от клавиатуры. Блок прослу- шивания регистрируется методом addKeyListener. Для интерфейса реали- зован класс-адаптер KeyAdapter |
| ListSelectionListener | javax.swing.event | Используется для обработки события, инициирующегося при изменении области выделения |
| MenuDragMouseListener | javax.swing.event | Интерфейс объявляет четыре метода обработчика событий, инициируемых при перемещении и сбросе в области компонентов меню |
| MenuKeyListener | javax.swing.event | Интерфейс используется для обработки событий, инициируемых для меню при вводе комбинаций клавиш |
| MenuListener | javax.swing.event | Определяет блок прослушивания с обработчиками событий для меню |
| MouseInputListener | javax.swing.event | Интерфейс наследует интерфейсам MouseMotionListener (методы: mouseDragged, mouseMoved) и MouseListener (методы: mouseClicked, mouseEntered, mouseExited, mousePressed, mouseReleased). Этот интерфейс используется для обработки любых событий, инициируемых действиями мыши |
| MouseListener | java.awt.event | Интерфейс, используемый для обработки событий от щелчков мышью. Блок прослушивания регистрируется методом addMouseListener. Для интерфейса реализован класс-адаптер MouseAdapter |
| MouseMotionListener | java.awt.event | Интерфейс, используемый для обработки событий от перемещения курсора мыши. Блок прослушивания регистри руется методом addMouseMotionListener. Для интерфейса реализован класс-адаптер MouseMotionAdapter. |
| PopupMenuListener | javax.swing.event | Интерфейс определяет методы блока прослушивания для всплывающих меню (popup-меню). |
| PropertyChangeListener | java.beans | Интерфейс определяет метод обработчик события, вызываемый при изменении свойства бина |
| TableColumnModelListener | javax.swing.event | Интерфейс, используемый для прослу шивания событий, изменяющих TableColumnModel. К таким событиям относятся добавление, удаление, изменение размера и перемещение столбца таблицы. Этот интерфейс реализован классами JTable, JTable.AccessibleJTable и JTableHeader |
| TableModelListener | javax.swing.event | Интерфейс, используемый для прослу шивания событий, которые изменяют TableModel |
| TextListener | java.awt.event | Интерфейс объявляет метод void textValueChanged(TextEvent e), вызываемый при изменении значения текстовых компонентов, таких, как TextArea, TextField |
| TreeExpansionListener | javax.swing.event | Интерфейс, используемый для прослу шивания событий распахивания или сворачивания дерева |
| TreeModelListener | javax.swing.event | Интерфейс, используемый для прослу шивания событий, которые изменяют TreeModel |
| TreeSelectionListener | javax.swing.event | Интерфейс объявляет метод, вызываемый при изменении выделенного элемента дерева |
| WindowListener | java.awt.event | Интерфейс используется для обработки событий окна. Блок прослушивания регистрируется методом addWindowListener. Для интерфейса реализован класс-адаптер WindowAdapter |
Блоки прослушивания
В настоящее время в языке Java используется модель ожидаемых событий. В этой модели инициируются только те события, которые были объявлены объектами как ожидаемые.Объект регистрирует блок прослушивания событий. Блок прослушивания - это интерфейс, который определяет набор методов-обработчиков событий, объявленных как ожидаемые события.
Регистрация блока прослушивания для компонента состоит в вызове метода, начинающегося с префикса add, за которым идет имя блока прослушивания. Имена блоков прослушивания оканчиваются суффиксом Listener.
Для одного компонента можно зарегистрировать только один блок прослушивания. Но один блок прослушивания может использоваться несколькими компонентами одновременно.
Для того, чтобы компонент мог обрабатывать поступающие для него события, следует:
Любой метод блока прослушивания имеет один параметр - объект, производный от класса EventObject.
В классе EventObject определен метод Object getSource(), возвращающий объект, который инициировал событие. Некоторые классы, производные от EventObject, имеют свои методы, которые определяют объект, инициировавший событие. Например, класс ComponentEvent определяет метод getComponent, возвращающий объект Component.
Классы-адаптеры
Чтобы зарегистрировать блок прослушивания, нужно указать его в объявлении класса как наследуемый. В результате этого класс, наследующий данный интерфейс, должен содержать код реализации всех объявленных в интерфейсе методов. Если таких методов много, а нужен только один, то все равно приходится включать код реализации каждого объявленного в интерфейсе метода. Альтернативой такому подходу служит применение классов адаптеров.Класс-адаптер для интерфейса блока прослушивания содержит "пустую" реализацию всех его методов. Таким образом, при создании класса, как производного от класса-адаптера, в качестве блока прослушивания можно указывать класс-адаптер и включать реализацию только необходимых методов.
На следующей схеме приведен пример обработки события "щелчок мышью" двумя способами: с применением интерфейса блока прослушивания и с применением адаптера блока прослушивания.
public class MyClass implements MouseListener { ... myObject.addMouseListener(this); ... /* Реализация всех методов интерфейса MouseListener */ public void mousePressed(MouseEvent e) { } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mouseClicked(MouseEvent e) { ...// Код метода } } | public class MyClass extends MouseAdapter { ... myObject.addMouseListener(this); ... /* Переопределение только одного метода адаптера MouseAdapter */ public void mouseClicked(MouseEvent e) } ...// Код переопределяемого метода } } |
Приведенный выше пример в практическом плане имеет один недостаток. При программировании классы приложений в большинстве случаев создаются как производные от классов Frame, Panel, JFrame, а классы апплетов - от класса Applet или JApplet. Но в языке Java реализовано только простое наследование, позволяющее иметь всего один наследуемый класс.
Поэтому, чтобы применять классы-адаптеры, можно использовать два способа реализации:
class MyClassAdapter extends MouseAdapter {//Объявление // внутреннего класса адаптера public void mouseClicked(MouseEvent e) { ... } // Реализация методов класса адаптера } }
public class MyClass extends Applet { ... // Создание объекта и реализация класса адаптера: myObject.addMouseListener(new MouseAdapter() { // Реализация переопределяемых методов класса адаптера public void mouseClicked(MouseEvent e) { } }); } }
Для того чтобы реализовать применение класса-адаптера с использованием вложенного класса, следует:
Для того чтобы реализовать применение класса адаптера с использованием вложенного анонимного класса-адаптера, следует:
Использование анонимных вложенных классов значительно улучшает читаемость кода.
События действия
Событие действия - это семантические событие, зависящее от типа компонента. Так, для командной кнопки событием действия будет щелчок мышью или нажатие клавиши Enter в момент, когда кнопка имеет фокус. Для компонентов типа "список" событием действия является выбор элемента, для компонентов "меню" - выбор пункта меню, а для компонентов "текстовое поле" - нажатие клавиши Enter.События действия определяются в интерфейсе ActionListener. Этот интерфейс задает единственный метод обработки события actionPerformed, вызываемый при возникновении для компонента сооответствующего его типу события действия.
Для того чтобы обрабатывать событие действия, можно выполнить следующее:
public class MyFrame extends Frame implements ActionListener { }.
myButton.addActionListener(this).
public void actionPerformed(ActionEvent e) { Toolkit.getDefaultToolkit().beep(); }.
Метод обработки события действия получает параметр типа ActionEvent.
Класс ActionEvent определен в иерархии классов Java следующим образом:

Класс ActionEvent определяет и наследует несколько полезных методов, включая следующие:
actionEvent.getModifiers() & ActionEvent. SHIFT_MASK.

События окна
Методы - обработчики событий окна объявляются в интерфейсе WindowListener . Для данного интерфейса существует класс адаптер WindowAdapter .Для того чтобы отслеживать все события окна, можно создать класс окна, наследуемый от интерфейса WindowListener.
Например:
public class MyWindowListener extends Frame implements WindowListener { public void windowClosing(WindowEvent e) { } public void windowClosed(WindowEvent e) { } public void windowOpened(WindowEvent e) { } public void windowIconified(WindowEvent e) { } public void windowDeiconified(WindowEvent e) { } public void windowActivated(WindowEvent e) { } public void windowDeactivated(WindowEvent e) { } }
Если надо отслеживать все события окна приложения или апплета, то блок прослушивания следует добавить для объекта this/.
Например:
addWindowListener(this);
Если надо отслеживать все события окна, создаваемого в процессе выполнения приложения или апплета, то сначала надо создать объект "окно", а затем добавить для него блок прослушивания.
Например:
myFrame = new Frame("Заголовок окна"); myFrame.addWindowListener(this);
Интерфейс WindowListene объявляет следующие методы, вызываемые для события WindowEvent:
События от клавиатуры
Методы обработчики событий от клавиатуры объявляются в интерфейсе KeyListener, для которого существует класс адаптер KeyAdapter.Событие от клавиатуры происходит для компонента, имеющего фокус. События от клавиатуры являются низкоуровневыми событиями.
Интерфейс KeyListener объявляет следующие методы обработчики для события KeyEvent:
o void keyTyped(KeyEvent e); // Ввод символа o void keyPressed(KeyEvent e); // Нажатие клавиши o void keyReleased(KeyEvent e) // Отпускание клавиши
Для получения информации о коде нажатой клавиши следует использовать методы класса KeyEvent.
Класс KeyEvent определен в иерархии классов Java следующим образом:

При нажатии или отпускании клавиши метод getKeyCode возвращает значение кода нажатой клавиши.
Метод getKeyChar возвращает значение символа Unicode или CHAR_UNDEFINED.
При обработки события ввода (метод void keyTyped(KeyEvent e) ) можно получить значение введенного с клавиатуры символа в формате Unicode или CHAR_UNDEFINED.
Клавиатурные комбинации обрабатываются в методах:
Например, нажатие клавиш Shift+B можно отследить в методе keyPressed (событие KEY_PRESSED): при первом вызове метода будет возвращен код клавиши Shift - VK_SHIFT, а при втором вызове метода - код клавиши A - VK_B. И только затем произойдет вызов метода keyTyped.
На следующей схеме приведен алгоритм обработки событий от клавиатуры.
| Shift+A | keyPressed | getKeyChar() getKeyCode() getModifiers() | символ = ' ' код = 16 (Shift) модификатор = 1 | ||
| keyPressed | getKeyChar() getKeyCode() getModifiers() | символ ='B' код = 66(B) модификатор = 1 | |||
| keyTyped | getKeyChar() getKeyCode() getModifiers() | символ ='B' код = 0 модификатор = 0 | |||
| keyReleased | getKeyChar() getKeyCode() getModifiers() | символ ='A' код = 66(B) модификатор = 0 | |||
| keyReleased | getKeyChar() getKeyCode() getModifiers() | символ = ' ' код = 16 (Shift) модификатор = 0 |
В классе KeyEvent определены виртуальные коды для всех клавиш клавиатуры. К таким кодам относятся:
Класс KeyEvent содержит следующий набор методов, предназначенных для определения кодов нажатых клавиш:
Наряду с описанными методами, можно использовать методы, наследуемые от класса java.awt.event.InputEvent, включая следующие:
Типы событий
Событием в языке Java является объект. Все события можно разделить на две группы: события низкого уровня и семантические события.К событиям низкого уровня относятся:
Семантические события являются событиями верхнего уровня и зависят от инициировавшего их компонента. Одно и то же семантическое событие может быть инициировано разными компонентами в ответ на различные действия пользователя. Семантическое событие можно рассматривать как более сложное событие, состоящее из одного или нескольких событий низкого уровня. Примером может служить событие MenuEvent, инициируемое при выборе пункта меню.
Введение в программирование
public class Frame1 extends JPanel
|
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Frame1 extends JPanel { JSplitPane jSplitPane1 = new JSplitPane(); // Класс // разделяемых панелей JScrollPane jScrollPane1 = new JScrollPane(); JScrollPane jScrollPane2 = new JScrollPane(); JTextPane jTextPane1 = new JTextPane(); JTextPane jTextPane2 = new JTextPane(); public Frame1() { try { jbInit(); } catch(Exception e) { } } public static void main(String s[]) { JFrame frame = new JFrame("Панели"); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); frame.setContentPane(new Frame1()); frame.pack(); frame.setVisible(true); } private void jbInit() throws Exception { // Определение ориентации разделяемых панелей jSplitPane1.setOrientation(JSplitPane.VERTICAL_SPLIT); // Размещение левого и правого компонента // на разделяемой панели jSplitPane1.setLeftComponent(jScrollPane1); jSplitPane1.setRightComponent(jScrollPane2); // Отображение кнопок сворачивания и разворачивания // сторон разделяемой панели jSplitPane1.setOneTouchExpandable(true); // Задание размера панелей jScrollPane1.setPreferredSize(new Dimension(300, 60)); jScrollPane2.setPreferredSize(new Dimension(300, 60)); // Добавление разделяемой панели к окну формы this.add(jSplitPane1, null); // Добавление компонентов в контейнеры // типа JScrollPane jScrollPane1.getViewport().add(jTextPane1, null); jScrollPane2.getViewport().add(jTextPane2, null); } } |
| Листинг 27.1. |
| Закрыть окно |
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Frame1 extends JPanel {
JSplitPane jSplitPane1 = new JSplitPane(); // Класс
// разделяемых панелей
JScrollPane jScrollPane1 = new JScrollPane();
JScrollPane jScrollPane2 = new JScrollPane();
JTextPane jTextPane1 = new JTextPane();
JTextPane jTextPane2 = new JTextPane();
public Frame1() { try { jbInit(); }
catch(Exception e) { } }
public static void main(String s[]) {
JFrame frame = new JFrame("Панели");
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0); } });
frame.setContentPane(new Frame1());
frame.pack();
frame.setVisible(true);
}
private void jbInit() throws Exception {
// Определение ориентации разделяемых панелей
jSplitPane1.setOrientation(JSplitPane.VERTICAL_SPLIT);
// Размещение левого и правого компонента
// на разделяемой панели
jSplitPane1.setLeftComponent(jScrollPane1);
jSplitPane1.setRightComponent(jScrollPane2);
// Отображение кнопок сворачивания и разворачивания
// сторон разделяемой панели
jSplitPane1.setOneTouchExpandable(true);
// Задание размера панелей
jScrollPane1.setPreferredSize(new Dimension(300, 60));
jScrollPane2.setPreferredSize(new Dimension(300, 60));
// Добавление разделяемой панели к окну формы
this.add(jSplitPane1, null);
// Добавление компонентов в контейнеры
// типа JScrollPane
jScrollPane1.getViewport().add(jTextPane1, null);
jScrollPane2.getViewport().add(jTextPane2, null);
}
}
public class MyApplet extends Applet
|
import java.applet.Applet; import java.awt.*; public class MyApplet extends Applet { Panel panel1 = new Panel(); GridLayout gridLayout1 = new GridLayout(); // Создание новой кнопки: Checkbox checkbox1 = new Checkbox(); Checkbox checkbox2 = new Checkbox(); GridLayout gridLayout2 = new GridLayout(); Panel panel2 = new Panel(); // Создание группы кнопок: CheckboxGroup checkboxGroup1 = new CheckboxGroup(); Checkbox checkbox4 = new Checkbox(); Checkbox checkbox5 = new Checkbox(); Checkbox checkbox6 = new Checkbox(); FlowLayout FlowLayout1 = new FlowLayout(); public MyApplet() { try { jbInit(); } catch(Exception e) { } } public static void main(String[] args) { MyApplet myApplet1 = new MyApplet(); } private void jbInit() throws Exception { setLayout(gridLayout2); panel1.setLayout(gridLayout1); checkbox1.setLabel("Флажок 1"); checkbox2.setLabel("Флажок 2"); checkbox2.setState(true); // Состояние флажка - // включен gridLayout2.setRows(2); panel2.setLayout(FlowLayout1); // Добавление в группу трех кнопок: checkbox4.setCheckboxGroup(checkboxGroup1); checkbox4.setLabel("Радиокнопка 1"); checkbox4.setState(true); // Состояние радиокнопки - // включена checkbox5.setCheckboxGroup(checkboxGroup1); checkbox5.setLabel("Радиокнопка 2"); checkbox6.setCheckboxGroup(checkboxGroup1); checkbox6.setLabel("Радиокнопка 3"); // Добавление кнопок в контейнеры - панели this.add(panel1, null); panel1.add(checkbox1, null); panel1.add(checkbox2, null); this.add(panel2, null); panel2.add(checkbox4, null); panel2.add(checkbox5, null); panel2.add(checkbox6, null); } } |
| Листинг 27.2. |
| Закрыть окно |
import java.applet.Applet;
import java.awt.*;
public class MyApplet extends Applet {
Panel panel1 = new Panel();
GridLayout gridLayout1 = new GridLayout();
// Создание новой кнопки:
Checkbox checkbox1 = new Checkbox();
Checkbox checkbox2 = new Checkbox();
GridLayout gridLayout2 = new GridLayout();
Panel panel2 = new Panel();
// Создание группы кнопок:
CheckboxGroup checkboxGroup1 = new CheckboxGroup();
Checkbox checkbox4 = new Checkbox();
Checkbox checkbox5 = new Checkbox();
Checkbox checkbox6 = new Checkbox();
FlowLayout FlowLayout1 = new FlowLayout();
public MyApplet() {
try { jbInit(); } catch(Exception e) { }
}
public static void main(String[] args) {
MyApplet myApplet1 = new MyApplet(); }
private void jbInit() throws Exception {
setLayout(gridLayout2);
panel1.setLayout(gridLayout1);
checkbox1.setLabel("Флажок 1");
checkbox2.setLabel("Флажок 2");
checkbox2.setState(true); // Состояние флажка -
// включен
gridLayout2.setRows(2);
panel2.setLayout(FlowLayout1);
// Добавление в группу трех кнопок:
checkbox4.setCheckboxGroup(checkboxGroup1);
checkbox4.setLabel("Радиокнопка 1");
checkbox4.setState(true); // Состояние радиокнопки -
// включена
checkbox5.setCheckboxGroup(checkboxGroup1);
checkbox5.setLabel("Радиокнопка 2");
checkbox6.setCheckboxGroup(checkboxGroup1);
checkbox6.setLabel("Радиокнопка 3");
// Добавление кнопок в контейнеры - панели
this.add(panel1, null);
panel1.add(checkbox1, null);
panel1.add(checkbox2, null);
this.add(panel2, null);
panel2.add(checkbox4, null);
panel2.add(checkbox5, null);
panel2.add(checkbox6, null);
}
}
к элементам списка через модель
|
String[] data = {"один", "два", "три"}; // Массив строк JList jList1 = new JList(data); // Создание списка, // содержащего массив строк // Доступ к элементам списка через модель for(int i = 0; i |
| Листинг 27.3. |
| Закрыть окно |
String[] data = {"один", "два", "три"}; // Массив строк
JList jList1 = new JList(data); // Создание списка,
// содержащего массив строк
// Доступ к элементам списка через модель
for(int i = 0; i
System.out.println(jList1.getModel().getElementAt(i)); }
// Заполнение списка данными, представляемыми классом Vector
JList jList1 = new JList();
Vector superClasses = new Vector();
Class rootClass = javax.swing.JList.class; // Создание объекта
// типа Class
for(Class cls = rootClass; cls != null;
cls = cls.getSuperclass())
{ // Получение всех подклассов
superClasses.addElement(cls); }
jList1.setListData(superClasses); // Заполнение компонента jList1
// списком всех его подклассов
// Добавление элементов в список, хранимый в объекте типа Vector
superClasses.addElement(new String("12345"));
// Выделение элементов списка:
jList1.setSelectedIndex(1); // Выделение второго элемента списка
jList1.getSelectedValue(); // Возвращает строку, отображаемую
//во втором элементе списка
Класс BorderLayout
Менеджер компоновки BorderLayout разбивает контейнер на пять областей и располагает добавляемые в контейнер объекты по краям (север, юг, запад, восток) и в центре.Каждая область указывается соответствующей константой: NORTH, SOUTH, EAST, WEST и CENTER. Если в методе add отсутствует строка, указывающая расположение компонента, то по умолчанию используется значение CENTER.
На рис. 27.1. приведен внешний вид, реализуемый менеджером компоновки BorderLayout для пяти кнопок, которые расположены в контейнере - апплете.

Рис. 27.1. Менеджер компоновки BorderLayout
Следующий код иллюстрирует использование компоновки BorderLayout:
import java.applet.Applet; import java.awt.*; public class MyApplet extends Applet { public MyApplet() { try { jbInit();} catch(Exception e) { } } public static void main(String[] args) { MyApplet myApplet1 = new MyApplet(); } private void jbInit() throws Exception { setLayout(new BorderLayout()); add(new Button("North"), BorderLayout.NORTH); add(new Button("South"), BorderLayout.SOUTH); add(new Button("East"), BorderLayout.EAST); add(new Button("West"), BorderLayout.WEST); add(new Button("Center"), BorderLayout.CENTER); } }
Класс BorderLayout предоставляет ряд методов, включая следующие:
Класс CardLayout
Класс CardLayout определяет менеджер компоновки для контейнера, который может содержать несколько страниц ("карт") и для которого одновременно может быть видна только одна карта.Класс CardLayout предоставляет ряд методов, включая следующие:
Например:
// Для контейнера типа JPanel void jButton1_actionPerformed(ActionEvent e) { ((CardLayout)jPanel1.getLayout()).next(jPanel1); } // Для контейнера типа Panel void button1_actionPerformed(ActionEvent e) { cardLayout1.next(panel1); }
Класс FlowLayout
Менеджер компоновки FlowLayout размещает добавляемые в контейнер компоненты последовательно слева направо. Компоненты могут быть размещены в нескольких последовательных рядах.На рис. 27.2 приведены два результата применения этой компоновки при изменении размеров контейнера.

Рис. 27.2. Применение компоновки FlowLayout
Класс FlowLayout предоставляет следующие константы, определяющие выравнивание компонентов:
Класс FlowLayout предоставляет ряд методов, включая следующие:
Класс GridBagLayout
Этот класс используется менеджером компоновки GridBagLayout и определяет требования к размещению компонентов.Компоновка GridBagLayout позволяет размещать компоненты в ячейках таблицы. Но, в отличие от менеджера компоновки GridLayout, ячейки таблицы могут различаться как по ширине, так и по высоте. Размещаемые компоненты могут занимать несколько ячеек.
Область, занимаемая компонентом, называется областью отображения. Ее размеры определяются значениями переменных gridwidth и gridheight (количество ячеек по горизонтали и по вертикали) класса GridBagConstraints.
Отступами (insets) называется расстояние между областью отображения и действительной областью, занимаемой компонентом.
На рис. 27.4 приведен пример компоновки, в которой кнопка 3 занимает 9 ячеек, но размер кнопки меньше размера области отображения.

Рис. 27.4. Отступы, устанавливаемые объектом GridBagConstraints
Если область отображения отличается от размера компонента, то для определения требований к размещению используется переменная fill.
Если размер размещаемого компонента меньше размера области отображения, то для указания размещения компонента используется переменная anchor, которая может принимать одно из следующих значений:
Переменная fill класса GridBagConstraint определяет, следует ли изменять размер компонента, и может принимать следующие значения:
Переменные gridheight и gridwidthкласса GridBagConstraint определяют число ячеек в столбце или строке соответственно. При этом константа GridBagConstraints.REMAINDER указывает, что компонент будет последним в столбце (строке), а константа GridBagConstraints.RELATIVE указывает, что компонент будет ближайшим к последнему.
Конструктор GridBagConstraints(int gridx, int gridy, int gridwidth, int gridheight, double weightx, double weighty, int anchor, int fill, Insets insets, int ipadx, int ipady) создает объект требований к размещению компонента, используемый менеджером компоновки, со всеми полями, имеющими заданные значения.
Класс GridLayout
Этот класс позволяет размещать компоненты в контейнере в виде таблицы. В каждой ячейке таблицы может быть размещен только один компонент.Размер всех ячеек таблицы одинаков. Количество строк и столбцов таблицы определяется или в конструкторе, или вызовом методов setColumns и setRows. При этом, если эти значения не равны нулю, то количество столбцов является величиной вычисляемой и зависит от общего числа компонентов, добавленных на компоновку, и указанного числа строк. И только в том случае, если количество строк задано равным нулю, заданное количество столбцов будет учитываться менеджером компоновки.
На рис. 27.3 приведены примеры использования компоновки GridLayout.

Рис. 27.3. Пример компоновки GridLayout
Для компоновки GridLayout следует определять или количество строк, или количество столбцов.
Например:
this.setLayout(gridLayout1); gridLayout1.setRows(3);
Кнопки
Кнопки могут располагаться в контейнере как отдельно, так и в группе.Пакет java.awt содержит следующие классы кнопок и групп:
Пакет облегченных swing-компонентов также предоставляет несколько классов кнопок и групп, включая следующие классы:
На кнопках из пакета javax.swing помимо метки может также отображаться и пиктограмма.
Класс Button позволяет создавать объекты "командные кнопки". При нажатии на кнопку JVM инициирует событие действия actionPerformed. Наряду с данным семантическим событием, инициируется ряд простых событий, таких как mouseClicked.
Класс Button предоставляет ряд методов, включая следующие:
Класс Checkbox позволяет создавать компоненты кнопки двух типов, называемые флажками и радиокнопками. Такой компонент может иметь два состояния: включено (true) и выключено (false). Каждый щелчок на компоненте изменяет его состояние на обратное.
Несколько переключателей могут быть объединены в группу, используя компонент CheckboxGroup, являющийся контейнером. Такая группа называется группой радиокнопок. Только один компонент группы может одновременно иметь состояние "включен".
Для обработки событий от кнопки используется интерфейс ItemListener. Этот интерфейс определяет всего один обработчик события - метод itemStateChanged (ItemEvent e).
Класс ItemEvent содержит метод getStateChange, позволяющий определить состояние кнопки. Метод может возвращать одно из двух возможных значений:
Например:
// Определение состояния кнопки void itemStateChanged(ItemEvent e) { if (e.getStateChange() == ItemEvent.SELECTED) { // Кнопка находится во включенном состоянии } }
Для добавления кнопки в группу для нее следует вызвать метод setCheckboxGroup, указав в качестве параметра объект типа CheckboxGroup.
Следующий листинг иллюстрирует создание флажков и группы радиокнопок:
import java.applet.Applet; import java.awt.*; public class MyApplet extends Applet { Panel panel1 = new Panel(); GridLayout gridLayout1 = new GridLayout(); // Создание новой кнопки: Checkbox checkbox1 = new Checkbox(); Checkbox checkbox2 = new Checkbox();
GridLayout gridLayout2 = new GridLayout(); Panel panel2 = new Panel(); // Создание группы кнопок: CheckboxGroup checkboxGroup1 = new CheckboxGroup(); Checkbox checkbox4 = new Checkbox(); Checkbox checkbox5 = new Checkbox(); Checkbox checkbox6 = new Checkbox(); FlowLayout FlowLayout1 = new FlowLayout(); public MyApplet() { try { jbInit(); } catch(Exception e) { } } public static void main(String[] args) { MyApplet myApplet1 = new MyApplet(); } private void jbInit() throws Exception { setLayout(gridLayout2); panel1.setLayout(gridLayout1); checkbox1.setLabel("Флажок 1"); checkbox2.setLabel("Флажок 2"); checkbox2.setState(true); // Состояние флажка - // включен gridLayout2.setRows(2); panel2.setLayout(FlowLayout1); // Добавление в группу трех кнопок: checkbox4.setCheckboxGroup(checkboxGroup1); checkbox4.setLabel("Радиокнопка 1"); checkbox4.setState(true); // Состояние радиокнопки - // включена checkbox5.setCheckboxGroup(checkboxGroup1); checkbox5.setLabel("Радиокнопка 2"); checkbox6.setCheckboxGroup(checkboxGroup1); checkbox6.setLabel("Радиокнопка 3"); // Добавление кнопок в контейнеры - панели this.add(panel1, null); panel1.add(checkbox1, null); panel1.add(checkbox2, null); this.add(panel2, null); panel2.add(checkbox4, null); panel2.add(checkbox5, null); panel2.add(checkbox6, null); } }
Листинг 27.2.
Классы кнопок пакета javax. swing наследуются от класса AbstractButton. Этот класс предоставляет ряд общих методов, включая следующие:
Например:
ImageIcon ImageIcon1= new ImageIcon("myicon.gif"); jButton1.setDisabledIcon(ImageIcon1);
Например:
jButton1.setMnemonic(KeyEvent.VK_A);
Для того чтобы определить в методе обработки события itemStateChanged(ItemEvent e), какой кнопкой было инициировано данное событие, следует использовать метод getItemSelectable.
Для того чтобы определить, является ли кнопка, инициировавшая событие, выделенной (находящейся в состоянии "включена" или "нажата"), следует использовать метод getStateChange.
Например:
if (e.getItemSelectable() == jCheckBox1 && e.getStateChange()==e.SELECTED) { }
Класс JToggleButton предназначен для создания кнопок, имеющих два состояния - "нажата" и "не нажата". Компонент JToggleButton может находится в зафиксированном нажатом состоянии (быть выделенным).
Этот класс является непосредственным суперклассом для классов JCheckbox и JRadioButton.
Для того чтобы установить состояние кнопки, следует использовать метод setSelected .
Например:
jToggleButton1.setSelected(true);
Кнопки JToggleButton могут быть объединены в группу. Такую группу следует предварительно создать, используя компонент типа javax.swing.ButtonGroup. Только одна из кнопок группы может одновременно иметь нажатое состояние.
Панели
Java-приложение создается как иерархия вложенных компонентов. Наверху этой иерархии могут находится компоненты классов Frame, JFrame, Applet или JApplet.Классы панелей используются как контейнеры для размещения других компонентов, в числе которых могут быть и сами панели. Пакет оконного интерфейса java.awt содержит один класс панели - Panel. Для добавления компонентов на панель класса Panel следует выполнить метод add, квалифицировав его именем панели.
Пакет облегченных swing-компонентов предоставляет несколько классов панелей, включая следующие классы:
При использовании менеджеров компоновки с панелями из пакета java.swing для получения ссылки на панель следует использовать метод getLayout.
Например:
((CardLayout)jPanel1.getLayout()).next(jPanel1);
Пакет javax.swing содержит интерфейс RootPaneContainer, реализуемый классами JApplet, JFrame, JInternalFrame, JWindow и JDialog.
При добавлении компонентов в контейнер, использующий интерфейс RootPaneContainer , метод add надо квалифицировать следующим образом:
frame.getContentPane().add(child).
Класс JRootPane используется всеми панелями пакета javax.swing. Он позволяет размещать содержимое панели на нескольких уровнях. JRootPane представояет четыре уровня:
Уровень contentPane должен быть родительским для размещения любого компонента. Для доступа к данному уровню используется метод getContentPane().
Уровень glassPane является самым верхним. Для доступа к данному уровню применяется метод getGlassPane().
Чтобы в приложении можно было использовать уровень glassPane, для него следует вызвать метод setGlassPane().
Например:
panel1.getContentPane().add(button1);
Компонент JTabbedPane реализует набор страниц, переключаемых по щелчку пользователя на вкладках. Вкладки добавляются методами addTab и insertTab. Вкладки нумеруются, начиная с 0.
Если количество вкладок равно нулю, то индекс текущей вкладки равен -1.
На рис. 27.5 показан внешний вид окна, содержащего панель вкладок класса JTabbedPane.

Рис. 27.5. Окно с компонентом JTabbedPane
Класс javax.swing.JScrollPane реализует компонент "прокручиваемая область".
Панель JScrollPane состоит из:
Чтобы изменить свойство области просмотра, ее следует получить, вызвав метод getViewport. Например, для задания цвета фона нужно записать:
scrollPane.getViewport().setBackground(Color.red).
На рис. 27.6 показано расположение основных частей прокручиваемой области JScrollPane.

Рис. 27.6. Окно с компонентом JTabbedPane
Контейнером для разделяемых панелей служит компонент JSplitPane. Разделяемые плавающие панели имеют одну общую сторону - по вертикали или по горизонтали.
Следующий пример иллюстрирует создание окна, в котором применяются разделяемые панели:
import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Frame1 extends JPanel { JSplitPane jSplitPane1 = new JSplitPane(); // Класс // разделяемых панелей JScrollPane jScrollPane1 = new JScrollPane(); JScrollPane jScrollPane2 = new JScrollPane(); JTextPane jTextPane1 = new JTextPane(); JTextPane jTextPane2 = new JTextPane(); public Frame1() { try { jbInit(); } catch(Exception e) { } } public static void main(String s[]) { JFrame frame = new JFrame("Панели"); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); frame.setContentPane(new Frame1()); frame.pack(); frame.setVisible(true); } private void jbInit() throws Exception { // Определение ориентации разделяемых панелей jSplitPane1.setOrientation(JSplitPane.VERTICAL_SPLIT); // Размещение левого и правого компонента // на разделяемой панели jSplitPane1.setLeftComponent(jScrollPane1); jSplitPane1.setRightComponent(jScrollPane2); // Отображение кнопок сворачивания и разворачивания // сторон разделяемой панели jSplitPane1.setOneTouchExpandable(true); // Задание размера панелей jScrollPane1.setPreferredSize(new Dimension(300, 60)); jScrollPane2.setPreferredSize(new Dimension(300, 60)); // Добавление разделяемой панели к окну формы this.add(jSplitPane1, null); // Добавление компонентов в контейнеры // типа JScrollPane jScrollPane1.getViewport().add(jTextPane1, null); jScrollPane2.getViewport().add(jTextPane2, null); } }
Листинг 27.1.
Применение компоновок
При проектировании интерфейса пользователя с использованием языка Java компоненты размещаются в контейнерах. Самым простым примером контейнера может служить окно формы (класс Frame). В общем случае любой класс, наследуемый от класса контейнера java.awt.Container, является контейнером.В языке Java для "плавающего" размещения компонентов, зависящего от размеров окна и размеров самих компонентов, введены классы компоновок.
Компоновка определяет порядок расположения компонентов на экране. Перед отображением объектов-контейнеров, содержащих другие компоненты, вызывается менеджер компоновки. Он располагает компоненты на экране в соответствующем порядке. Используемый менеджер компоновки указывается вызовом метода setLayout. Если менеджер компоновки не указан явно, то выбирается тот, что используется по умолчанию для данного класса контейнера.
Для того чтобы отключить использование менеджеров компоновки и перейти к явному указанию координат, следует вызвать метод setLayuot(null).
Пакеты java.awt и javax.swing предоставляют следующие классы менеджеров компоновки:
Для использования конкретного менеджера компоновки следует создать объект данного класса и установить созданную компоновку для данного контейнера, а затем добавить в контейнер другие компоненты.
Например:
GridBagLayout gridBagLayout1 = new GridBagLayout(); this.setLayout(gridBagLayout1); Button button1 = new Button(); button1.setLabel("Кнопка 1"); this.add(button1, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 315, 0));
Метод setLayout следует квалифицировать именем контейнера, для которого устанавливается компоновка. Для класса контейнера, в котором выполняется данный метод, можно использовать ссылку this. В случае предварительного создания объекта "менеджер компоновки", его следует указать как параметр метода setLayout. В противном случае в качестве параметра метода setLayout следует указать оператор создания анонимного объекта "менеджер компоновки".
Например:
this.setLayout(gridLayout1); // или this.setLayout(new GridLayout());
При использовании панели типа Panel методы объекта "менеджер компоновки" вызываются стандартным образом имя_объекта_менеджер_компоновки.имя_метода.
Например:
cardLayout1.show(Panel1,"Panel0");.
При использовании панели типа JPanel методы объекта "менеджер компоновки" вызываются с использованием метода getLayout.
Например:
((CardLayout)jPanel2.getLayout()).show(jPanel2,"jPanel0");
Списки
Списки позволяют отображать группу элементов в один столбец, предоставляя пользователю возможность выбора элемента.Библиотека JDK содержит ряд классов списков, включая следующие:
При выделении элемента в списке или в ниспадающем списке (или отмене выделения) инициируется событие itemStateChanged. В метод обработки этого события передается объект типа ItemEvent. Используя свойство SELECTED для объекта типа ItemEvent, можно определить, выделен ли элемент списка. Интерфейс ItemListener описывает метод обработки события itemStateChanged.
При двойном щелчке мышью на элементе списка (или нажатии клавиши Enter при выделенном элементе списка) для компонента типа List, JList или JComboBox инициируется событие actionPerformed. В метод обработки этого события передается объект типа ActionEvent. Интерфейс ActionListener описывает метод обработки события actionPerformed.
Заполнение списков можно выполнять в обработчике события фрейма windowOpened. Добавление элементов в список List и Choice выполняется методом add.
Например:
List list1 = new List(4, false); // Создание списка // с 4 видимыми строками и с запретом множественного выбора list1.add("Строка 1"); // Добавление элементов в список list1.add("Строка 2"); list1.add("Строка 3"); list1.add("Строка 4"); list1.add("Строка 5"); list1.add("Строка 6"); add(list1); // Добавление списка в текущий контейнер
Список JList позволяет выделять один или несколько элементов. Содержание списка представляется моделью ListModel. Доступ к элементам списка реализуется с использованием модели. Для заполнения списка используется метод setListData.
Список JList непосредственно не поддерживает прокрутку списка. Реализовать скроллинг можно двумя способами:
JScrollPane scrollPane = new JScrollPane(jList1);
JScrollPane scrollPane = new JScrollPane(); scrollPane.getViewport().(jList1);
Например:
String[] data = {"один", "два", "три"}; // Массив строк JList jList1 = new JList(data); // Создание списка, // содержащего массив строк // Доступ к элементам списка через модель for(int i = 0; i

Текстовые компоненты
Текстовые компоненты предназначаются для ввода и отображения строк.Библиотека JDK предоставляет следующие классы текстовых компонентов:
Все текстовые компоненты пакета java.awt наследуются от класса TextComponent.
Все текстовые компоненты пакета javax.swing наследуются от класса JTextComponent.
Для отображения текста, который может быть изменен только программным путем, служат компоненты .Label из пакета java.awt и JLabel из пакета javax.swing.
Компонент JLabel также можно использовать и для отображения рисунков.
Большинство названий методов, предоставляемых классами TextComponent и JTextComponent для работы с текстом, совпадают. Так, для того чтобы получить строку, которая содержит весь текст, расположенный в текстовом компоненте, можно использовать метод getText, а для получения позиции ввода - метод getCaretPosition; для определения, можно ли редактировать текст, - метод isEditable, для выделения текста в указанном диапазоне - метод select, а для выделения всего текста - метод selectAll.
При каждом изменении значения текстового компонента, включая ввод каждого символа, происходит событие textValueChanged. При нажатии на клавишу Enter для текстового поля инициируется событие actionPerformed. Но при перемещении фокуса ввода событие actionPerformed не происходит.
для ввода и редактирования одной строки текста. Этот класс предоставляет ряд методов, включая следующие:
Любой вводимый пользователем символ будет экранирован данным эхо-символом.
Хотя компоненты пакета javax.swing и называются облегченными, но они предоставляют значительно большие возможности, чем традиционные текстовые компоненты пакета java.awt.
Управление текстом, отображаемым в текстовом компоненте, определяется интерфейсом Document. Этот интерфейс предназначен для определения методов работы с текстом. Интерфейс Documentреализован классом AbstractDocument.
Текст в компоненте типа JTextComponent представляется ассоциируемой с ним текстовой моделью, определяющей как содержание, так и стиль.
Фактически, текстовый компонент предоставляет доступ к:
Класс DefaultEditorKit и StyledEditorKit описывают набор именованных действий, которые можно использовать для создания на основе текстового компонента редактора текста.
В классе DefaultEditorKit определен набор предоставляемых текстовому компоненту действий, таких как перемещение курсора, выделение или вставка текста.
Например, для создания пункта меню, выполняющего форматирование выделенного диапазона текста, и изменения размера шрифта на 14p, следует записать:
menu.add(new StyledEditorKit.FontSizeAction ( "Имя пункта меню", 14));
Константы класса DefaultEditorKit описывают набор именованных действий, включая следующие:
Для того чтобы добавить в документ строку в отформатированном виде, можно использовать метод insertS класса DefaultStyledDocument. Этот класс является подклассом класса AbstractDocumentи реализует интерфейсы Document и StyledDocument.
Интерфейс StyledDocument определяет методы для работы со стилем документа. Так, для назначения диапазону текста набора атрибутов стиля можно использовать метод setCharacter-Attributes, а для определения используемого стиля или шрифра - методы getStyle и getFont.
Атрибуты стиля определяются интерфейсом AttributeSet. Этот интерфейс реализован классом SimpleAttributeSet.
Для создания набора атрибутов стиля следует создать переменную типа SimpleAttributeSet и установить атрибуты стиля, используя методы класса
Например:
SimpleAttributeSet attrSt = new SimpleAttributeSet(); StyleConstants.setBold(attrSt, true); StyleConstants.setFontSize(attrSt, 12); StyleConstants.setForeground(attrSt, Color.red);
При добавлении строки в текстовый документ вызовом метода insertString класса Default-StyledDocument один из параметров ссылается на набор аттрибутов.
Фактически набор атрибутов стиля накладывается на диапазон текста. Если текстовый компонент содержит текст с различным форматированием, то на каждый отдельный диапазон текста должен быть "наложен" свой набор атрибутов стиля.
Для создания таблицы ключей акселераторов, используемых для текстового компонента, следует:
Например:
JTextPane textPane; .... Keymap keymap = textPane.addKeymap("MyKeymap", textPane.getKeymap());
Например:
Action action = getActionByName(DefaultEditorKit.downAction);
Например:
KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_N, Event.CTRL_MASK);
Например:
keymap.addActionForKeyStroke(key, action);
Класс StyleConstants предоставляет набор методов, позволяющих определять или устанавливать значения для атрибутов форматирования.
Набор атрибутов форматирования может быть создан как объект одного из следующих классов:
Например:
...SimpleAttributeSet[] attrs = new SimpleAttributeSet[stringForPane.length +1]; // Создание набора атрибутов: attrs[0] = new SimpleAttributeSet(); StyleConstants.setFontFamily(attrs[0], "SansSerif"); // Установка значения атрибута StyleConstants.setFontSize(attrs[0], 12); attrs[1] = new SimpleAttributeSet(attrs[0]); StyleConstants.setFontSize(attrs[1], 14); StyleConstants.setBold(attrs[1], true); // Добавление строки в документ с заданным набором // атрибутов (defaultStyleDocument1 переменная // класса, наследуемого от DefaultStyleDocument) defaultStyleDocument1.insertString( defaultStyleDocument1.length, string1, attrs[1]);
Введение в программирование
CGI и ISAPI приложения
Данные, отображаемые web-браузером, представляют собой HTML-страницу.По HTTP-запросу web-браузер посылает на web-сервер информацию, содержащую URL-адрес документа, тип запроса и значения параметров. URL-адрес может указывать как простую HTML-страницу и приложение, выполняемое на web-сервере. Такое приложение иногда называется серверным приложением.
К серверным приложениям относятся CGI-приложения и ISAPI-приложения.
В отличии от CGI-приложений, выполняемых в отдельном процессе, ISAPI-приложения реализуются как DLL-библиотеки.
Результатом выполнения CGI или ISAPI-приложения чаще всего является динамически сформированная HTML-страница.
только на тот случай, если
| // Заголовочный файл MyISAPI_1.h #pragma once #include "resource.h" class CMyISAPI_1Extension : public CHttpServer { public: CMyISAPI_1Extension(); // Конструктор ~CMyISAPI_1Extension(); public: virtual BOOL GetExtensionVersion(HSE_VERSION_INFO* pVer); virtual BOOL TerminateExtension(DWORD dwFlags); void Default(CHttpServerContext* pCtxt); DECLARE_PARSE_MAP() }; // Файл реализации MyISAPI_1.cpp #include "stdafx.h" #include "MyISAPI_1.h" CWinApp theApp; // Объект "приложение" BEGIN_PARSE_MAP(CMyISAPI_1Extension, CHttpServer) // Таблица // обработки команды // TODO: место для определения ON_PARSE_COMMAND() и // ON_PARSE_COMMAND_PARAMS() ON_PARSE_COMMAND(Default, CMyISAPI_1Extension, ITS_EMPTY) DEFAULT_PARSE_COMMAND(Default, CMyISAPI_1Extension) END_PARSE_MAP(CMyISAPI_1Extension) CMyISAPI_1Extension theExtension; // Только один объект //ISAPI-расширение класса, // наследуемого от CHttpServer CMyISAPI_1Extension::CMyISAPI_1Extension(){ } // Конструктор CMyISAPI_1Extension::~CMyISAPI_1Extension() { } BOOL CMyISAPI_1Extension::GetExtensionVersion(HSE_VERSION_INFO* pVer) { // Вызов метода базового класса CHttpServer::GetExtensionVersion(pVer); // Загрузка строки описания TCHAR sz[HSE_MAX_EXT_DLL_NAME_LEN+1]; ISAPIVERIFY(::LoadString(AfxGetResourceHandle(), // Макро - если IDS_SERVER, sz, HSE_MAX_EXT_DLL_NAME_LEN)); // 0,то завершение _tcscpy(pVer->lpszExtensionDesc, sz); return TRUE; } BOOL CMyISAPI_1Extension::TerminateExtension(DWORD dwFlags) { // Метод класса CHttpServer - позволяет выполнить завершение // потоков и работы ISAPI-расширения return TRUE; } // CMyISAPI_1Extension : методы обработчики // Код формируемой HTML-страницы записывается методом Default // в поток вывода void CMyISAPI_1Extension::Default(CHttpServerContext* pCtxt) { StartContent(pCtxt); // Начало HTML-страницы WriteTitle(pCtxt); // Формирование значения тега TITLE // _T - для Unocode конвертируется в L *pCtxt << _T(" HTML-page from "); // Первая строка //HTML-страницы *pCtxt << _T("ISAPI-application "); // Формирование строки HTML-документа для отображения формы *pCtxt << _T(" |
| Листинг 28.1. |
| Закрыть окно |
// Заголовочный файл MyISAPI_1.h
#pragma once
#include "resource.h"
class CMyISAPI_1Extension : public CHttpServer
{
public:
CMyISAPI_1Extension(); // Конструктор
~CMyISAPI_1Extension();
public:
virtual BOOL GetExtensionVersion(HSE_VERSION_INFO* pVer);
virtual BOOL TerminateExtension(DWORD dwFlags);
void Default(CHttpServerContext* pCtxt);
DECLARE_PARSE_MAP()
};
// Файл реализации MyISAPI_1.cpp
#include "stdafx.h"
#include "MyISAPI_1.h"
CWinApp theApp; // Объект "приложение"
BEGIN_PARSE_MAP(CMyISAPI_1Extension, CHttpServer) // Таблица
// обработки команды
// TODO: место для определения ON_PARSE_COMMAND() и
// ON_PARSE_COMMAND_PARAMS()
ON_PARSE_COMMAND(Default, CMyISAPI_1Extension, ITS_EMPTY)
DEFAULT_PARSE_COMMAND(Default, CMyISAPI_1Extension)
END_PARSE_MAP(CMyISAPI_1Extension)
CMyISAPI_1Extension theExtension; // Только один объект
//ISAPI-расширение класса,
// наследуемого от CHttpServer
CMyISAPI_1Extension::CMyISAPI_1Extension(){ } // Конструктор
CMyISAPI_1Extension::~CMyISAPI_1Extension() { }
BOOL CMyISAPI_1Extension::GetExtensionVersion(HSE_VERSION_INFO* pVer)
{ // Вызов метода базового класса
CHttpServer::GetExtensionVersion(pVer);
// Загрузка строки описания
TCHAR sz[HSE_MAX_EXT_DLL_NAME_LEN+1];
ISAPIVERIFY(::LoadString(AfxGetResourceHandle(), // Макро - если
IDS_SERVER, sz, HSE_MAX_EXT_DLL_NAME_LEN)); // 0,то завершение
_tcscpy(pVer->lpszExtensionDesc, sz);
return TRUE;
}
BOOL CMyISAPI_1Extension::TerminateExtension(DWORD dwFlags)
{ // Метод класса CHttpServer - позволяет выполнить завершение
// потоков и работы ISAPI-расширения
return TRUE;
}
// CMyISAPI_1Extension : методы обработчики
// Код формируемой HTML- страницы записывается методом Default
// в поток вывода
void CMyISAPI_1Extension::Default(CHttpServerContext* pCtxt)
{
StartContent(pCtxt); // Начало HTML-страницы
WriteTitle(pCtxt); // Формирование значения тега TITLE
// _T - для Unocode конвертируется в L
указатель на строку, значение типа
|
BEGIN_PARSE_MAP(CDerivedClass, CHttpServer) DEFAULT_PARSE_COMMAND(Myfunc, CDerivedClass) // Для запроса типа // http://LOCALSERVER/MyISAPI_1.dll?Myfunc&string1&135 ON_PARSE_COMMAND(Myfunc, // Имя функции CDerivedClass, // Имя класса ITS_PSTR ITS_I2) // Список из двух параметров: // указатель на строку, значение типа short ON_PARSE_COMMAND_PARAMS("string integer=42") // Для запроса с тремя параметрами ON_PARSE_COMMAND(Myfunc2, CDerivedClass, ITS_PSTR ITS_I2 ITS_PSTR) ON_PARSE_COMMAND_PARAMS("string integer string2='Default value'") DEFAULT_PARSE_COMMAND(Myfunc3, CDerivedClass) ON_PARSE_COMMAND(Myfunc3, CDerivedClass, ITS_RAW) // Различное число параметров END_PARSE_MAP(CDerivedClass) // Функции, выполняемые для обработки команд void Myfunc(CHttpServerContext* pCtxt, LPTSTR pszName, int nNumber) { } // Первый параметр стандартен для всех функций, // обрабатывающих команды, тип второго и третьего // параметра был указан в макросе ON_PARSE_COMMAND void Myfunc2(CHttpServerContext* pCtxt, LPTSTR pszName, int nNumber, LPTSTR pszTitle) { } void CDerivedClass::Myfunc3( // Используется тип параметров // ITS_RAW CHttpServerContext* pCtxt, void* pVoid, // pVoid - указатель на передаваемые данные DWORD dwBytes) // dwBytes - количество переданных байтов данных { } |
| Листинг 28.2. |
| Закрыть окно |
BEGIN_PARSE_MAP(CDerivedClass, CHttpServer)
DEFAULT_PARSE_COMMAND(Myfunc, CDerivedClass)
// Для запроса типа
// http://LOCALSERVER/MyISAPI_1.dll?Myfunc&string1&135
ON_PARSE_COMMAND(Myfunc, // Имя функции
CDerivedClass, // Имя класса
ITS_PSTR ITS_I2) // Список из двух параметров:
// указатель на строку, значение типа short
ON_PARSE_COMMAND_PARAMS("string integer=42")
// Для запроса с тремя параметрами
ON_PARSE_COMMAND(Myfunc2,
CDerivedClass,
ITS_PSTR ITS_I2 ITS_PSTR)
ON_PARSE_COMMAND_PARAMS("string integer string2='Default value'")
DEFAULT_PARSE_COMMAND(Myfunc3, CDerivedClass)
ON_PARSE_COMMAND(Myfunc3, CDerivedClass,
ITS_RAW) // Различное число параметров
END_PARSE_MAP(CDerivedClass)
// Функции, выполняемые для обработки команд
void Myfunc(CHttpServerContext* pCtxt, LPTSTR pszName, int nNumber)
{ } // Первый параметр стандартен для всех функций,
// обрабатывающих команды, тип второго и третьего
// параметра был указан в макросе ON_PARSE_COMMAND
void Myfunc2(CHttpServerContext* pCtxt, LPTSTR pszName,
int nNumber, LPTSTR pszTitle)
{ }
void CDerivedClass::Myfunc3( // Используется тип параметров
// ITS_RAW
CHttpServerContext* pCtxt,
void* pVoid, // pVoid - указатель на передаваемые данные
DWORD dwBytes) // dwBytes - количество переданных байтов данных
{ }
cs using System; using
|
// Default.aspx. cs using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } } // Default.aspx <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> |