Студопедия

КАТЕГОРИИ:

АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

Символические константы: #define




Директива #define может быть записана в двух синтаксических формах:

#define идентификатор текст
#define идентификатор (список параметров) текст

Данная директива заменяет все дальнейшие вхождения идентификатора на текст. Подобный процесс называется макроподстановкой. Текст может быть любым фрагментом программы на СИ, а также может и отсутствовать.
Если в качестве первого символа в строке программы используется символ #, то эта строка является командной строкой препроцессора (макропроцессора). Командная строка препроцессора заканчивается символом перевода на новую строку. Если непосредственно перед концом строки поставить символ обратной косой черты "\", токомандная строка будет продолжена на следующую строку программы.
Директива #define, подобно всем директивам препроцессора, начинается c символа # в самой левой позиции. Она может появиться в любом месте исходного файла, а даваемое определение имеет силу от места появления до конца файла. Мы активно используем эту директиву для определения символических констант в наших примерах программ, однако она имеет более широкое применение, что мы покажем дальше.

33. Включение текстов файлов, директива #include

Директива #include включает в программу содержимое определенного файла. Эта директива может быть представлена в двух формах:

#include «имя файла»
#include <имя файла>

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

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

34. Условная компиляция, директивы #if, #ifdef, #ifndef, #elif, #else, #endif.

Условная компиляция обеспечивается в языке C++ набором команд, которые, по существу, управляют не компиляцией, а препроцессорной обработкой:

#if <константное_выражение>

#ifdef <идентификатор>

#ifndef <идентификатор>

#else

#endif

#elif

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

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

#if/#ifdef/#ifndef <константное_выражение или идентификатор>

      <текст_1>

#else //необязательная директива

     <текст_2>

#endif

Конструкция #else <текст_2> не обязательна. Текст_1 включается в компилируемый текст только при истинности проверяемого условия. Если условие ложно, то при наличии директивы #else на компиляцию передается текст_2. Если директива #else отсутствует, то весь текст от #if до #endif при ложном условии опускается. Различие между формами команд #if состоит в следующем.

В первой из перечисленных директив #if проверяется значение константного целочисленного выражения. Если оно отлично от нуля, то считается, что проверяемое условие истинно. Например, в результате выполнения директив:

#if 5+12

     <текст_1>

#endif

текст_1 всегда будет включен в компилируемую программу.

В директиве #ifdef проверяется, определен ли с помощью команды #define к текущему моменту идентификатор, помещенный после #ifdef. Если идентификатор определен, то текст_1 используется компилятором.

В директиве #ifndef проверяется обратное условие - истинным считается неопределенность идентификатора, т.е. тот случай, когда идентификатор не был использован в команде #define или его определение было отменено командой#undef.

Условную компиляцию удобно применять при отладке программ для включения или исключения контрольных печатей. Например,

#define DE 1 //Определение идентификатора.

. . . . .

#ifdef DE //Проверка определения идентификатора.

     cout << "Отладочная печать";

#endif

Таких печатей, появляющихся в программе в зависимости от определенности идентификатора DE может быть несколько и, убрав директиву #define DE 1, сразу же отключаем все отладочные печати.

Файлы, предназначенные для препроцессорного включения в модули программы, обычно снабжают защитой отповторного включения. Такое повторное включение может произойти, если несколько модулей, в каждом из которых запланировано препроцессорное включение одного и того же файла, объединяются в общий текст программы. Например, такими средствами защиты снабжены все заголовочные файлы (подобные iostream.h) стандартной библиотеки. Схема защиты от повторного включения может быть такой (фрагмент файла с именем filename):

#ifndef _FILE_NAME //Если константа не определена

    //Включаемый текст файла filename.

#define _FILE_NAME //Определение идентификатора.

#endif

Здесь _FILE_NAME - зарезервированный для файла filename препроцессорный идентификатор, который не должен встречаться в других текстах программы. При первом включении содержимого этого файла в программу идентификатор_FILE_NAME не определен, поэтому происходит включение этого файла. В конце этого файла происходит определение этого идентификатора с помощью директивы #define. При повторном обращении к тексту этого файла включения не произойдет, так как выполнение директивы #ifndef _FILE_NAME выработает ложный результат.

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

#elif <константное_выражение>

которая является сокращением конструкции #else #if.

Структура исходного текста с применением этой директивы такова:

#if <константное_выражение_1>

     <текст_1>

#elif <константное_выражение_2>

     <текст_2>

#elif <константное_выражение_3>

     <текст_3>

. . . .

#else

     <текст_N>

#endif

Препроцессор проверяет вначале условие в директиве #if, если оно ложно (равно 0) - вычисляетконстантное_выражение_2, если оноравно О - вычисляется константное_выражение_3 и т.д. Если все выражения ложны, то в компилируемый текст включается текст для случая #else. В противном случае, т.е. при появлении хотя бы одного истинного выражения (в #if или в #elif), начинает обрабатываться текст, расположенный непосредственно за этой директивой, а все остальные директивы не рассматриваются. Таким образом, препроцессор обрабатывает всегда только один из участков текста, выделенных командами условной компиляции.

35. Макроподстановки средствами препроцессора. Директивы # и ##

Пустая директива состоит из строки, в которой содержится единственный символ #. Эта директива всегда игнорируется препроцессором.

36. Парадигмы программирования, объектная модель.

Паради́гма программи́рования — это система идей и понятий, определяющих стиль написания компьютерных программ, а также образ мышления программиста.

Важно отметить, что парадигма программирования не определяется однозначно языком программирования; практически все современные языки программирования в той или иной мере допускают использование различных парадигм. Так на языке Си, который не является объектно-ориентированным, можно работать в соответствии с принципами объектно-ориентированного программирования, хотя это и сопряжено с определёнными сложностями; функциональное программирование можно применять при работе на любом императивном языке, в котором имеются функции (для этого достаточно не применять присваивание), и т.д.

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

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

1. наличием объектов и возможностью описания динамики системы как реакции на сообщения, пересылаемыми между объектами;

2. возможностью создания новых классов;

3. наличием механизма наследования для расширения или уточнения поведения имеющихся классов;

4. наличием полиморфизма между классами или объектами.

37. Составные части объектной модели: абстрагирование, инкапсуляция, модульность, иерархия, типизация, параллелизм, перманентность.

Абстракция выделяет существенные характеристики некоторого объекта, отличающие его от всех других видов объектов и, таким образом, четко определяет его концептуальные границы с точки зрения наблюдателя.

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

Выделение полного и достаточного набора абстракций при решении задачи с применением объектного подхода представляет собой главную задачу объектно-ориентированного проектирования.

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

• Абстракция сущности Объект представляет собой полезную модель некой сущности в предметной области

• Абстракция поведения Объект состоит из обобщенного множества операций

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

§ Пользователь может взаимодействовать с объектом только через этот интерфейс. Реализуется с помощью ключевого слова: public.

§ Пользователь не может использовать закрытые данные и методы. Реализуется с помощью ключевых слов: private, protected, internal.

Инкапсуляция — один из четырёх важнейших механизмов объектно-ориентированного программирования (наряду с абстракцией,полиморфизмом и наследованием).

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

Инкапсуляция — это процесс отделения друг от друга элементов объекта, определяющих его устройство и поведение; инкапсуляция служит для того, чтобы изолировать контрактные обязательства абстракции от их реализации.

Модульность в языках программирования — принцип, согласно которому программное средство (ПС, программа, библиотека, web-приложение и др.) разделяется на отдельные именованные сущности, называемые модулями. Модульность часто является средством упрощения задачи проектирования ПС и распределения процесса разработки ПС между группами разработчиков. При разбиении ПС на модули для каждого модуля указывается реализуемая им функциональность, а также связи с другими модулями.

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

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

Иерархия — это упорядочение абстракций путем расположения их по уровням. В объектно-ориентированных системах используются два вида иерархических структур: структуры классов ( иерархические отношения «is a») и структуры объектов (отношения вида «part of»).

Отношения вида «is a» реализуются в объектно-ориентированных языках с помощью наследования или генерализации.

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

Типизация — это способ защититься от использования объектов одного класса вместо другого, или по крайней мере управлять таким использованием.

Центральное место в типизации занимают механизмы согласования типов. Конкретный язык программирования может иметь сильный или слабый механизм типизации, или даже не иметь его вовсе. Сильно типизированные языки непреклонно следуют правилам использования типов.

Не следует путать понятия сильной (строгой) и статической типизации. Строгая типизация призвана обеспечить соответствие типов, а статическая типизация (так же называемая статическим или ранним связыванием), определяет время, когда имена (переменные ссылочного типа или указатели) связываются с типами адресуемых ими объектов. При статическом связывании тип адресуемого объекта, ровно как и тип результата любого выражения, определяется уже на стадии компиляции.

Параллелизм — это свойство, отличающее активные объекты от пассивных.

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

Параллелизм может обеспечиваться как средствами языка (Java, Ada,

Smalltalk), так и специально написанными библиотеками, которые используются при написании параллельной системы с использованием языков, не имеющих встроенной поддержки этого принципа (C++).

38. Преимущества объектной модели

Существует пять преимуществ, которые дает объектная модель.
Во-первых, объектная модель позволяет в полной мере использовать выразительные возможности объектных и объектно-ориентированных языков программирования. Страуструп отмечает: "Не всегда очевидно, как в полной мере использовать преимущества такого языка, как C++. Существенно повысить эффективность и качество кода можно просто за счет использования C++ в качестве "улучшенного C" с элементами абстракции данных. Однако гораздо более значительным достижением является введение иерархии классов в процессе проектирования. Именно это называется OOD и именно здесь преимущества C++ демонстрируются наилучшим образом" [82]. Опыт показал, что при использовании таких языков, как Smalltalk, Object Pascal, C++, CLOS и Ada вне объектной модели, их наиболее сильные стороны либо игнорируются, либо применяются неправильно.
Сохраняемость поддерживает состояние и класс объекта в пространстве и во времени.
Во-вторых, использование объектного подхода существенно повышает уровень унификации разработки и пригодность для повторного использования не только программ, но и проектов, что в конце концов ведет к созданию среды разработки [83]. Объектно-ориентированные системы часто получаются более компактными, чем их не объектно-ориентированные эквиваленты. А это означает не только уменьшение объема кода программ, но и удешевление проекта за счет использования предыдущих разработок, что дает выигрыш в стоимости и времени.
В-третьих, использование объектной модели приводит к построению систем на основе стабильных промежуточных описаний, что упрощает процесс внесения изменений. Это дает системе возможность развиваться постепенно и не приводит к полной ее переработке даже в случае существенных изменений исходных требований.
В-четвертых, в главе 7 показано, как объектная модель уменьшает риск разработки сложных систем, прежде всего потому, что процесс интеграции растягивается на все время разработки, а не превращается в единовременное событие. Объектный подход состоит из ряда хорошо продуманных этапов проектирования, что также уменьшает степень риска и повышает уверенность в правильности принимаемых решений.
Наконец, объектная модель ориентирована на человеческое восприятие мира, или, по словам Робсона, "многие люди, не имеющие понятия о том, как работает компьютер, находят вполне естественным объектно-ориентированный подход к системам".

39. Объектно-ориентированный подход к проектиро­ванию и разработке программ

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

Объектно-ориентированное проектирование состоит в описании структуры и поведения проектируемой системы, то есть, фактически, в ответе на два основных вопроса:

§ Из каких частей состоит система.

§ В чём состоит ответственность каждой из частей.

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

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

Большое значение имеет правильное построение иерархии классов. Одна из известных проблем больших систем, построенных по ООП-технологии — так называемая проблема хрупкости базового класса. Она состоит в том, что на поздних этапах разработки, когда иерархия классов построена и на её основе разработано большое количество кода, оказывается трудно или даже невозможно внести какие-либо изменения в код базовых классов иерархии (от которых порождены все или многие работающие в системе классы). Даже если вносимые изменения не затронут интерфейс базового класса, изменение его поведения может непредсказуемым образом отразиться на классах-потомках. В случае крупной системы разработчик базового класса не просто не в состоянии предугадать последствия изменений, он даже не знает о том, как именно базовый класс используется и от каких особенностей его поведения зависит корректность работы классов-потомков.

 

 

40. Понятия «объект» и «класс». Свойства объекта: состояние, поведение, идентичность

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

Введение объектов преследует две цели:

– понимание прикладной задачи (проблемы); введение основы для реализации на компьютере.

Каждый объект имеет определенное время жизни.

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

Объект (по Гради Бучу) — это мыслимая или реальная сущность, обладающая характерным поведением и отличительными характеристиками и являющаяся важной в предметной области.

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

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

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

Для каждого объекта существует определенный набор действий, которые с ним можно произвести.

Пример: операции с файлом.

Результат выполнения действий зависит от состояния объекта на момент совершения действия, т.е. нельзя, например, удалить файл, если он открыт кем-либо (заблокирован).

В то же время действия могут менять внутреннее состояние объекта — при открытии или закрытии файла свойство «открыт» принимает значения «да» или «нет», соответственно.

Программа, написанная с использованием ООП, обычно состоит из множества объектов, и все эти объекты взаимодействуют между собой.

В терминологии объектно-ориентированного подхода понятия «действие», «сообщение» и «метод» являются синонимами.

Поведение (behavior) — действия и реакции объекта, выраженные в терминах передачи сообщений и изменения состояния; видимая извне и воспроизводимая активность объекта.

Уникальность — это то, что отличает объект от других объектов.

Пример: банкноты.

В машинном представлении под параметром уникальности объекта чаще всего понимается адрес размещения объекта в памяти.

Уникальность объекта состоит в том, что всегда можно определить, указывают две ссылки на один и тот же объект или на разные объекты.

Все банкноты принадлежат одному и тому же классу объектов (именно с этим связана их одинаковость). Номинальная стоимость, материал, форма — это атрибуты класса.

Совокупность атрибутов и их значений характеризует объект. Наряду с термином «атрибут» часто используют термины «свойство», «реквизит» и «поле».

Все объекты одного и того же класса описываются одинаковыми наборами атрибутов. Однако объединение объектов в классы определяется не наборами, а семантикой.

«Конюшня» и «лошадь» могут иметь одинаковые атрибуты: цена и возраст.

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

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

Каждый класс также может иметь специальные методы, которые автоматически вызываются при создании и уничтожении объектов этого класса:

– конструктор (constructor) - выполняется при создании объектов;

- деструктор (destructor) - выполняется при уничтожении объектов.

41. Класс как расширение понятия структуры. Данные и методы класса в языке C++

Операция - это функция (или преобразование), которую можно применять к объектам данного класса.

Одна и та же операция может, вообще говоря, применяться к объектам разных классов: такая операция называется полиморфной, так как она может иметь разные формы для разных классов. Например, для объектов классов вектор и комплексное число можно определить операцию +; эта операция будет полиморфной, так как сложение векторов и сложение комплексных чисел, вообще говоря, разные операции.

Каждой операции соответствует метод - реализация этой операции для объектов данного класса. Таким образом, операция - это спецификация метода, метод - реализация операции. Например, в классе файл может быть определена операция печать (print). Эта операция может быть реализована разными методами: (а) печать двоичного файла; (б) печать текстового файла и др. Логически эти методы выполняют одну и ту же операцию, хотя реализуются они разными фрагментами кода.

Каждая операция имеет один неявный аргумент - объект к которому она применяется. Кроме того, операция может иметь и другие аргументы (параметры). Эти дополнительные аргументы параметризуют операцию, но не связаны с выбором метода. Метод связан только с классом и объектом (некоторые объектно-ориентированные языки, например C++, допускают одну и ту же операцию с разным числом аргументов, причем используя то или иное число аргументов, мы практически выбираем один из методов, связанных с такой операцией; на этапе предварительного проектирования системы удобнее считать эти операции различными, давая им разные имена, чтобы не усложнять проектирование).

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

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

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

Значения некоторых атрибутов объекта могут быть доступны только операциям этого объекта. Такие атрибуты называются закрытыми. Для задания класса необходимо указать имя этого класса, а затем перечислить его атрибуты и операции (или методы).

42. Доступность компонентов класса в языке C++. Статические компоненты класса

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

 

Свойства (properties) компонент представляют собой расширение понятия членов данных и хотя не хранят данные как таковые, однако обеспечивают доступ к членам данных объекта. C++Builder использует ключевое слово _propertyдля объявления свойств. При помощи событий (events) компонента сообщает пользователю о том, что на нее оказано некоторое предопределенное воздействие. Основная сфера применения методов в программах, разрабатываемых в среде C++Builder -это обработчики событий (event handlers), которые реализуют реакцию программы на возникновение определенных событий. Легко заметить некоторое сходство событий и сообщений операционной системы Windows. Типичные простые события —нажатие кнопки или клавиши на клавиатуре. Компоненты инкапсулируют свои свойства, методы и события.

На первый взгляд компоненты ничем не отличаются от других объектных классов языка C++, за исключением ряда особенностей, среди которых пока отметим следующие:

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

• Все компоненты являются прямыми или косвенными потомками одного общего класса-прародителя (TComponent).

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

• Компоненты размещаются только в динамической памяти кучи (heap) с помощью оператораnew, а не на стеке, как объекты обычных классов.

• Свойства компонент заключают в себе RTTI - идентификацию динамических типов.

• Компоненты можно добавлять к Палитре компонент и далее манипулировать с ними посредством Редактора форм интегрированной среды визуальной разработки C++Builder.

ООП интерпретирует взаимодействие с объектами как посылку запросов некоторому объекту или между объектами. Объект, принявший запрос, реагирует вызовом соответствующего метода. В отличие от других языков ООП, таких как SmallTalk, C++ не поощряет использование понятия "запрос". Запрос - это то, что делается с объектом, а метод - это то, как объект реагирует на поступивший запрос.

При ближайшем рассмотрении метод оказывается обычной функцией-членом, которая включена в определение класса. Чтобы вызвать метод, надо указать имя функции в контексте данного класса или в обработчике некоторого события.

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

43. Указатель this в языке C++

Указатель thisявляется константным только в constметодах. Нужен для того, чтобы в методе не возникало неопределенности когда поле класса и параметры метода имеют одиноковое имя . this -> x = x; Данные у каждого объекта свои, а функции класса общие для всех объектов,
указатель this помогает, определить с данными какого объекта будет работать функция. Вот этот-то самый указатель, именующий индивидуальную структуру данных для каждого объекта и называется "указатель this". Семантически он соответствует адресу "всего экземпляра объекта". И когда компилятор компилирует код метода класса, то все ссылки на свои данные экземпляра (например, на переменную "x") компилятор по умолчанию трактует как this->x. Т.о. внутри метода класса this есть "указатель на самого себя". Естественно, что внутри каждого экземпляра всякого класса this будет иметь своё значение - его заранее не знает программист, зато - очень хорошо знает компилятор. И его обязательно получит и любой нестатический метод объекта.

Но сам метод - "не обслуживается" this. Методы-то класса всегда разделяются всеми экземплярами класса. И уничтожаются - только данные класса, а методы неуничтожимы. Поэтому вызов конструкции delete this вполне корректен, он заставит метод класса разрушить данный экземпляр данных (объект) класса, но не сам этот метод - другие экземпляры того же класса не пострадают.

44. Друзья классов в языке C++

Предположим, вы определили два класса, vector и matrix (вектор и матрица). Каждый скрывает свое представление и предоставляет полный набор действий для манипуляции объектами его типа. Теперь определим функцию, умножающую матрицу на вектор. Для простоты допустим, что в векторе четыре элемента, которые индексируются 0...3, и что матрица состоит из четырех векторов, индексированных 0...3. Допустим также, что доступ к элементам вектора осуществляется через функцию elem(), которая осуществляет проверку индекса, и что в matrix имеется аналогичная функция. Один подход состоит в определении глобальной функции multiply() (перемножить) примерно следующим образом:

vector multiply(matrix& m, vector& v);

{

vector r;

for (int i = 0; i<3; i++) { // r[i] = m[i] * v;

     r.elem(i) = 0;

     for (int j = 0; j<3; j++)

         r.elem(i) += m.elem(i,j) * v.elem(j);

}

return r;

}

Это своего рода "естественный" способ, но он очень неэффективен. При каждом обращении к multiply() elem() будет вызываться 4*(1+4*3) раза.

Теперь, если мы сделаем multiply() членом класса vector, мы сможем обойтись без проверки индексов при обращении к элементу вектора, а если мы сделаем multiply() членом класса matrix, то мы сможем обойтись без проверки индексов при обращении к элементу матрицы. Однако членом двух классов функция быть не может. Нам нужно средство языка, предоставляющее функции право доступа к закрытой части класса. Функция не член, получившая право доступа к закрытой части класса, называется другом класса (friend). Функция становится другом класса после описания как friend. Например:

 

 

class matrix;

 

class vector {

float v[4];

// ...

friend vector multiply(matrix&, vector&);

};

 

class matrix {

vector v[4];

// ...

friend vector multiply(matrix&, vector&);

};

Функция друг не имеет никаких особенностей, помимо права доступа к закрытой части класса. В частности, friend функция не имеет указателя this (если только она не является полноправным членом функцией). Описание friend - настоящее описание. Оно вводит имя функции в самой внешней области видимости программы и сопоставляется с другими описаниями этого имени. Описание друга может располагаться или в закрытой, или в открытой части описания класса; где именно, значения не имеет.
Теперь можно написать функцию умножения, которая использует элементы векторов и матрицы непосредственно:

vector multiply(matrix& m, vector& v);

{

vector r;

for (int i = 0; i<3; i++) { // r[i] = m[i] * v;

     r.v[i] = 0;

     for (int j = 0; j<3; j++)

         r.v[i] += m.v[i][j] * v.v[j];

}

return r;

}

 

Есть способы преодолеть эту конкретную проблему эффективности не используя аппарат friend (можно было бы определить операцию векторного умножения и определить multiply() с ее помощью). Однако существует много задач, которые проще всего решаются, если есть возможность предоставить доступ к закрытой части класса функции, которая не является членом этого класса. В Главе 6 есть много примеров применения friend. Достоинства функций друзей и членов будут обсуждаться позже.
Функция член одного класса может быть другом другого. Например:

 

class x {

// ...

void f();

};

 

class y {

// ...

friend void x::f();

};

Нет ничего необычного в том, что все функции члены одного класса являются друзьями другого. Для этого есть даже более краткая запись:

 

class x {

friend class y;

// ...

};

Такое описание friend делает все функции члены класса y друзьями x.

 

45. Понятие конструкторов и деструктора класса в языке C++

С++ обеспечивает удобные способы создания и удаления объектов, через специальные процедуры. Процедуры создания объектов называются конструкторами, а процедуры уничтожения - деструкторами. Конструкторы автоматически вызываются при описании объекта, а деструкторы - при выходе из блока, в котором этот объект был описан. Если необходимые конструкторы или деструктор для класса не описаны, то транслятор создает их сам. Для того, чтобы понять важность конструкторов, сделаем маленькое отступление. Общей проблемой для всех традиционных языков программирования является инициализация: прежде чем использовать структуру данных, нужно выделить ей память и проинициализировать.

конструкторы используются для создания объекта. Благодаря конструкторам разрабатываемые новые типы данных становятся такой неотъемлимой частью языка, как и встроенные типы. Конструктор определяет, каким образом новый объект типа класса будет создан, как будет проводиться выделение памяти и инициализация объекта. Его определение может включать инструкции для выделения памяти, присваивания значений элементам, преобразование типов, и все что угодно, необходимое для данного класса. При этом конструкторы наследуют имя своего класса.

Конструкторы могут вызываться неявно - через обращение к функции элементу во время объявления объекта. Задавая объекту параметры, вы сообщаете о их передаче непосредственно конструктором.

Конструкторы могут вызываться во время, когда выполняется обращение к объекту. Так, конструктор вызывается всякий раз, когда обращаются к функции элементу объекта.

Деструкторы, как следует из их названия, уничтожают объекты класса, созданные перед этим конструктором, очищая значения и освобождая память. Деструкторы наследуют имена своих классов, с добавлением лидирующего знака тильда '~'.

Как и конструкторы, деструкторы могут вызываться явно (при помощи оператора С++ delete) или неявно - при выходе объекта из области действия.

46. Виды конструкторов в языке C++

В объектно-ориентированном программировании конструктор класса (от англ. constructor, иногда сокращают ctor) — специальный блок инструкций, вызываемый при создании объекта.

Конструктор схож с методом, но отличается от метода тем, что не имеет явным образом определённого типа возвращаемых данных, не наследуется, и обычно имеет различные правила для рассматриваемых модификаторов. Конструкторы часто выделяются наличием одинакового имени с именем класса, в котором объявляется. Их задача — инициализировать члены объекта и определитьинвариант класса, сообщив в случае некорректности инварианта. Корректно написанный конструктор оставит объект в «правильном» состоянии. Неизменяемые объекты тоже должны быть проинициализированы конструктором.

Термин «конструктор» также используется для обозначения одного из тегов, описывающих данные в алгебраическом типе данных. Это использование несколько отличается от описываемого в статье. Для дополнительной информации смотрите Алгебраический тип данных.

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

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

§ конструктор по умолчанию — конструктор, не принимающий аргументов;

§ конструктор копирования — конструктор, принимающий в качестве аргумента объект того же класса (или ссылку из него);

§ конструктор преобразования — конструктор, принимающий один аргумент (эти конструкторы могут вызываться автоматически для преобразования значений других типов в объекты данного класса).















Конструктор копирования

Основная статья: Конструктор копирования

Конструктор, аргументом которого является ссылка на объект того же класса. Применяется в C++ для передачи объектов в функциипо значению.

Конструктор копирования в основном необходим, когда объект имеет указатели на объекты выделенные в куче. Если программист не создаёт конструктор копирования, то компилятор создаст неявный конструктор копирования, который копирует указатели как есть, то есть фактическое копирование данных не происходит и два объекта ссылаются на одни и те же данные в куче. Соответственно попытка изменения «копии» повредит оригинал, а вызов деструктора для одного из этих объектов при последующем использовании другого приведёт к обращению в область памяти, уже не принадлежащую программе.

Аргумент должен передаваться именно по ссылке, а не по значению. Это вытекает из коллизии: при передаче объекта по значению (в частности, для вызова конструктора) требуется скопировать объект. Но для того, чтобы скопировать объект, необходимо вызвать конструктор копирования.

Конструктор преобразования

Конструктор, принимающий один аргумент. Задаёт преобразование типа своего аргумента в тип конструктора. Такое преобразование типа неявно применяется только если оно уникально.

Виртуальный конструктор

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

«Виртуальными конструкторами» называют похожий, но другой механизм, присутствующий в некоторых языках — например, он есть в Delphi, но нет в C++ и Java. Этот механизм позволяет создать объект любого заранее неизвестного класса при двух условиях:

§ этот класс является потомком некоего наперёд заданного класса (в данном примере это класс TVehicle);

§ на всём пути наследования от базового класса к создаваемому цепочка переопределения не обрывалась. При переопределении виртуального метода синтаксис Delphi требует ключевое слово overload, чтобы старая и новая функции с разными сигнатурами могли сосуществовать, override для переопределения функции либо reintroduce для задания новой функции с тем же именем — последнее недопустимо.

47. Порядок вызова конструкторов и деструкторов в языке C++

В большинстве случаев при работе с объектами сразу после создания осуществляется их инициализация – заполнение полей объекта начальными значениями.

Для того чтобы инициализация объектов осуществлялась более просто и надежно, используются специальные методы – конструкторы.

Конструкторы определяют, каким образом будет создан новый объект, как будет проводиться распределение памяти и инициализация объекта.

Общий вид объявления конструкторов:

classИмяКласса {

public:

ИмяКласса(параметры);

...

};

Отличия конструкторов от обычных методов:

1. Имя конструктора всегда должно совпадать с именем класса

2. При объявлении конструктора запрещается указывать тип возвращаемого значения (даже void).

3. Конструкторы обычно объявляются в разделе public.

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

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

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

Общий вид конструктора по умолчанию:

classИмяКласса {

public:

ИмяКласса();

...

};

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

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

Пример. Конструктор по умолчанию для класса "Точка"

class Point {

private:

double x, y;

public:

Point();

...

};

Point::Point() {

x= 0;

y= 0;

}

Конструктор по умолчанию автоматически вызывается при объявлении переменной-объекта:

PointA;// Вызов конструктора по умолчанию

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

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

– при объявлении массива объектов (Point m[10]);

– при наследовании;

– при работе с некоторыми шаблонами и т.п.










Последнее изменение этой страницы: 2018-04-12; просмотров: 317.

stydopedya.ru не претендует на авторское право материалов, которые вылажены, но предоставляет бесплатный доступ к ним. В случае нарушения авторского права или персональных данных напишите сюда...