Объектно-ориентированное программирование
Общая характеристика объектно-ориентированного подхода в современном программировании. Рассмотрение концепций и понятия объектно-ориентированного подхода, а также их выражения на унифицированном языке моделирования UML и языке программирования С++.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | курс лекций |
Язык | русский |
Дата добавления | 01.10.2014 |
Размер файла | 256,9 K |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Состояние объекта характеризуется перечнем (обычно статическим) всех свойств данного объекта и текущими (обычно динамическими) значениями каждого из этих свойств. В число свойств входят атрибуты объекта и атрибуты всех его агрегированных частей.
Одним из свойств торгового автомата является способность принимать монеты. Это статическое (фиксированное) свойство, в том смысле, что оно - существенная характеристика торгового автомата. С другой стороны, этому свойству соответствует динамическое значение, характеризующее количество принятых монет. Сумма увеличивается по мере опускания монет в автомат и уменьшается, когда продавец забирает деньги из автомата.
В некоторых случаях значения свойств объекта могут быть статическими (например, заводской номер автомата), поэтому в данном определении использован термин "обычно динамическими".
К числу свойств объекта относятся присущие ему или приобретаемые им характеристики, черты, качества или способности, делающие данный объект самим собой. Например, для лифта характерным является то, что он сконструирован для поездок вверх и вниз, а не горизонтально.
Перечень свойств объекта является, как правило, статическим, поскольку эти свойства составляют неизменяемую основу объекта. Мы говорим "как правило", потому что в ряде случаев состав свойств объекта может изменяться. Примером может служить робот с возможностью самообучения. Робот первоначально может рассматривать некоторое препятствие как статическое, а затем обнаруживает, что это дверь, которую можно открыть. В такой ситуации по мере получения новых знаний изменяется создаваемая роботом модель мира.
Все свойства имеют некоторые значения. Эти значения могут быть простыми количественными характеристиками, а могут ссылаться на другой объект. Состояние лифта может описываться числом 3, означающим номер этажа, на котором лифт в данный момент находится. Состояние торгового автомата описывается в терминах других объектов, например имеющихся в наличии напитков. Конкретные напитки - это самостоятельные объекты, отличные от торгового автомата.
3.2 Поведение
Объекты не существуют изолированно, а подвергаются воздействию или сами воздействуют на другие объекты.
Поведение - это то, как объект действует и реагирует; поведение выражается в терминах состояния объекта и передачи сообщений. Поведение объекта - это его наблюдаемая и проверяемая извне деятельность.
Операция - это услуга, которую можно запросить у любого объекта класса для воздействия на его поведение.
Например, клиент может активизировать операции push и pop для того, чтобы управлять объектом-стеком (добавить или изъять элемент).
В чисто объектно-ориентированном языке принято говорить о передаче сообщений между объектами. В C++ мы говорим, что один объект вызывает функцию-член другого. В основном понятие сообщение совпадает с понятием операции над объектами.
Передача сообщений - это один уровень, задающий поведение. Из нашего определения следует, что состояние объекта также влияет на его поведение.
Рассмотрим торговый автомат. Мы можем сделать выбор, но поведение автомата будет зависеть от его состояния. Если мы не опустили в него достаточную сумму, скорее всего ничего не произойдет. Если же денег достаточно, автомат выдаст нам желаемое (и тем самым изменит свое состояние).
Некоторые операции изменяют состояние. В связи с вышесказанным можно заключить, что состояние объекта представляет суммарный результат его поведения.
Операция - это услуга, которую класс может предоставить своим клиентам. На практике типичный клиент совершает над объектами операции следующих видов:
- модификатор - это операция, которая изменяет состояние объекта;
- селектор - это операция, считывающая состояние объекта, но не меняющая состояния;
- конструктор - это операция создания объекта и/или его инициализации; в С++ конструктор имеет то же имя, что и класс;
- деструктор - это операция, освобождающая ресурсы, которые использует объект, и/или разрушающая сам объект; в С++ имя деструктора состоит из имени класса, перед которым ставится знак "тильда" - "~".
Две последние операции являются универсальными. Они обеспечивают инфраструктуру, необходимую для создания и уничтожения экземпляров класса.
Если у класса есть конструктор, то он вызывается всегда, когда создается объект класса. Если у класса есть деструктор, то он вызывается всегда, когда объект класса уничтожается.
Объекты могут создаваться следующим образом:
- автоматический объект создается каждый раз, когда его описание встречается при выполнении программы, и уничтожается каждый раз при выходе из блока, в котором оно появилось;
- статический объект создается один раз, при запуске программы, и уничтожается один раз, при ее завершении;
- объект в свободной памяти создается с помощью операции new и уничтожается с помощью операции delete;
- объект-член создается как подобъект другого класса.
Пример.
Расширим описание класса Stack, с тем чтобы программист мог задавать максимальный размер каждого создаваемого стека (размер массива s).
class Stack {
public:
Stack (int n); // конструктор, n - максимальный размер
~Stack ( ); // деструктор
void push (int el); // модификатор
int pop ( ); // модификатор
bool isFull ( ) const; // селектор
bool isEmpty ( ) const; // селектор
. . .
};
Конструктор с одним аргументом может служить также для преобразования типа своего аргумента в тип конструктора.
Пример. Рассмотрим определение класса complex.
class complex {
double re, im;
public:
complex (double r, double i);
complex (double r);
. . .
};
Мы определили два конструктора, один из которых имеет один аргумент и служит для инициализации комплексного числа (его действительной части) значением вещественного числа. Теперь мы можем записать два эквивалентных оператора
complex a = complex (1);
complex a = 1;
Можно запретить использование конструктора для таких преобразований, объявив его с ключевым словом explicit.
class Stack {
public:
explicit Stack (int n); // конструктор, задающий максимальный
. . .// размер стека, не используется для преобразования
};
Для инициализации отдельных частей объекта с помощью конструктора служат инициализаторы конструктора. Важность инициализаторов в том, что только с их помощью можно инициализировать константные члены и члены, являющиеся ссылками. Используется синтаксис следующего примера:
class Х {
const int i;
Stack &ps;
X(int ii, Stack &s): i(ii), ps(s){. . .} // i получает значение ii, а ps - s
};
В чисто объектно-ориентированных языках определять процедуры и функции вне классов не допускается. В гибридных языках, выросших из процедурных языков, таких как C++, допускается описывать операции как независимые от объектов подпрограммы.
Операции, определенные вне классов, называют свободными подпрограммами. В C++ они называются функциями-нечленами.
bool check_stack (Stack & my_stack, int el)
{
Stack temp_stack;
. . .// используя дополнительный стек temp_stack, проверить,
//есть ли в my_stack элемент el
}
Свободные подпрограммы - это процедуры и функции, которые выполняют роль операций высокого уровня над объектом или объектами одного или разных классов. Свободные процедуры обычно группируются в соответствии с классами, для которых они создаются.
3.3 Идентичность
Идентичность - это такое свойство объекта, которое отличает его от всех других объектов.
Источником ошибок в объектно-ориентированном программировании является неумение отличать имя объекта от самого объекта.
Пример.
Определим точку на плоскости.
struct Point {
int х; // первая координата
int у; // вторая координата
Point (void); // конструктор по умолчанию (0,0)
Point (int xValue, int yValue); // конструктор
};
Наша абстракция Point - это пара координат (х,у). Предусмотрено два конструктора: один инициализирует точку нулевыми значениями координат, а другой - некоторыми заданными значениями.
Теперь определим точку, отображаемую на экране дисплея (DisplayPoint). Ограничимся возможностями рисовать точку и перемещать ее по экрану, а также запрашивать ее положение. Мы записываем нашу абстракцию в виде следующего объявления на C++:
class DisplayPoint {
public:
DisplayPoint ( ); // конструктор по умолчанию (0,0)
DisplayPoint (const Point& location); // конструктор
~DisplayPoint ( ); // деструктор
void draw ( ); // рисует точку на экране
void move (const Point& location); // перемещает точку
Point location ( ); // возвращает координаты
. . .
};
Аргументы некоторых функций указаны с модификатором const. Он указывает, что значение объекта, передаваемого по ссылке или указателю, в функции не изменится. Литералы, константы и аргументы, требующие преобразования типа, можно передавать как const&-аргументы и нельзя - в качестве не const &-аргументов.
Объявим экземпляры класса DisplayPoint:
DisplayPoint Item1;
DisplayPoint * Item2 = new DisplayPoint (Point (75,75));
DisplayPoint * Item3 = new DisplayPoint (Point (100,100));
DisplayPoint * Item4 = 0;
При выполнении этих операторов возникают четыре имени и три разных объекта (рис. 3.1 а). В памяти будут отведены четыре места под имена Item1, Item2, Item3, Item4. При этом Item1 будет именем объекта класса DisplayPoint, а три других - указателями. Кроме того, лишь Item2 и Item3 будут на самом деле указывать на объекты класса. У объектов, на которые указывают Item2 и Item3, к тому же нет имен, хотя на них можно ссылаться "разыменовывая" соответствующие указатели (например, *Item2). Поэтому мы можем сказать, что Item2 указывает на отдельный объект класса, на имя которого мы можем косвенно ссылаться через *Item2.
Уникальная идентичность каждого объекта сохраняется на все время его существования, даже если его внутреннее состояние изменилось. При этом имя объекта не обязательно сохраняется.
Рассмотрим результат выполнения следующих операторов (рис. 3.1, б):
Item1.move (Item2 -> location ( ));
Item4 = Item3;
Item4 -> move (Point(38, 100));
Объект Item1 и объект, на который указывает Item2, теперь относятся к одной и той же точке экрана. Указатель Item4 стал указывать на тот же объект, что и Item3. Хотя объект Item1 и объект, на который указывает Item2, имеют одинаковое состояние, они остаются разными объектами. Кроме того, мы изменили состояние объекта *Item3, использовав его новое косвенное имя Item4.
Рис. 3.1 Идентичность объектов
Ситуацию, когда объект именуется более чем одним способом несколькими синонимичными именами, называют структурной зависимостью.
Структурная зависимость порождает в объектно-ориентированном программировании много проблем. Трудность распознания побочных эффектов при действиях с синонимичными объектами часто приводит к утечкам памяти, неправильному доступу к памяти и, хуже того, непрогнозируемому изменению состояния. Например, если мы уничтожим объект через указатель Item3, то значение указателя Item4 окажется бессмысленным: эта ситуация называется висячей ссылкой. Рассмотрим результат выполнения следующих действий (рис. 3.1, в):
Item2 = &Item1;
Item4 -> move (Item2 -> location( ) );
В первой строке создается синоним: Item2 указывает на тот же объект, что и Item1. Во второй доступ к состоянию Item1 получен через этот новый синоним. К сожалению, при этом произошла утечка памяти: объект, на который первоначально указывала ссылка Item2, не именуется ни прямо, ни косвенно и его идентичность потеряна.
В языках типа C++ такая память освобождается только тогда, когда завершается программа, создавшая объект. Такие утечки памяти могут вызвать и просто неудобство, и крупные сбои, особенно если программа должна непрерывно работать длительное время. Представьте себе утечку памяти в программе управления спутником. Перезапуск компьютера на спутнике в нескольких миллионах километров от Земли очень неудобен.
Для создания нового объекта, имеющего то же состояние, что и у существующего, необходимо вызвать конструктор копирования, имеющий следующее описание:
DisplayPoint (const DisplayPoint &); // конструктор копирования
Отсутствие этого специального конструктора вызывает копирующий конструктор, действующий по умолчанию, который копирует объект поэлементно. Это разумно не всегда. Когда объект содержит ссылки или указатели на другие объекты, такая операция приводит к созданию синонимов указателей на эти объекты.
Пример. Модифицируем описание класса DisplayPoint так, чтобы каждый его экземпляр содержал указатель на точку:
class DisplayPoint {
. . .
Point * DPoint
. . .
};
. . .
DisplayPoint Item1;
. . .
DisplayPoint Item2(Item1);
Поэлементное копирование объекта Item1 приведет к тому, что указатели на агрегированные объекты типа Point у обоих объектов Item1 и Item2 будут указывать на один и тот же объект, содержащий местоположение отображаемой точки (рис. 3.2). Фактически, оба объекта будут ответственны за отображение одной и той же точки. Этого ли мы хотели достичь?
Присваивание - это тоже копирование и в C++ его смысл можно изменять. Например, мы могли бы добавить в определение класса DisplayPoint следующую строку:
DisplayPoint operator= (const DisplayPoint &);
Теперь мы можем записать
Рис. 3.2 Поэлементное копирование
DisplayPoint Item5;
Item5 = Item1;
Как и в случае копирующего конструктора, если оператор присваивания не переопределен явно, то по умолчанию объект копируется поэлементно.
Присваивание тесно связано с равенством. Равенство можно понимать двумя способами. Во-первых, два имени могут обозначать один и тот же объект. Во-вторых, это может быть равенство состояний у двух разных объектов. В примере, приведенном на рис. 3.1, в, для Item1 и Item2 справедлив первый вариант тождественности. А для Item2 и Item3 истинным будет второй вариант.
В С++ нет предопределенного оператора равенства, поэтому мы должны определить равенство и неравенство, объявив эти операторы при описании:
int operator == ( Point&);
int operator != ( Point&);
3.4 Отношения между объектами
Сами по себе объекты не представляют никакого интереса: только в процессе их взаимодействия реализуется система. Например, самолет - это "совокупность элементов, каждый из которых по своей природе стремится упасть на землю, но за счет совместных непрерывных усилий преодолевающих эту тенденцию". Он летит только благодаря согласованным усилиям своих компонентов.
Отношения двух любых объектов основываются на предположениях, которыми один обладает относительно другого: об операциях, которые можно выполнять, и об ожидаемом поведении. Особый интерес для объектно-ориентированного анализа и проектирования представляют два типа отношений между объектами: связь и агрегация.
Связь - это семантическое соединение между объектами. Объект сотрудничает с другими объектами, посылая сообщения через связи, соединяющие его с ними. Связь - это специфическое сопоставление, через которое клиент запрашивает у объекта-сервера услугу или через которое один объект находит путь к другому.
Пусть есть два объекта А и В и связь между ними. Чтобы А мог послать В сообщение, В должен быть в каком-то смысле видим для А.
Перечислим следующие четыре способа обеспечить видимость:
- сервер глобален по отношению к клиенту;
- сервер (или указатель на него) передан клиенту в качестве параметра операции;
- сервер является частью клиента;
- сервер локально порождается клиентом в ходе выполнения какой-либо операции.
Если связи обозначают равноправные или "клиент-серверные" отношения между объектами, то агрегация описывает отношения целого и части, приводящие к соответствующей иерархии объектов, причем, идя от целого (агрегата), мы можем прийти к его частям (атрибутам).
Пример. Рассмотрим класс объектов, управляющих температурой в теплице Controller. Пусть он имеет атрибут h класса Heater (нагреватель).
class Controller {
Heater h;
. . .
};
В данном случае Controller - целое, а h - его часть (часть его состояния). Исходя из Controller, можно найти соответствующий нагреватель. Однако по h нельзя найти содержащий его объект (называемый также его контейнером), если только сведения о нем случайно не являются частью состояния h.
Агрегация может означать физическое вхождение одного объекта в другой, но не обязательно. Самолет состоит из крыльев, двигателей, шасси и прочих частей. С другой стороны, отношения акционера с его акциями - это агрегация, которая не предусматривает физического включения. Акционер монопольно владеет своими акциями, но они в него не входят физически.
4. Классы
Понятия класса и объекта настолько тесно связаны, что невозможно говорить об объекте безотносительно к его классу. Однако существует важное различие этих двух понятий. В то время как объект обозначает конкретную сущность, определенную во времени и в пространстве, класс определяет лишь абстракцию существенного в объекте.
Класс - это множество объектов, имеющих общую структуру и общее поведение. Любой конкретный объект является экземпляром класса.
Пример. Рассмотрим сходства и различия между следующими классами: цветы, маргаритки, красные розы, желтые розы, лепестки и пчелы. Мы можем заметить следующее:
- маргаритка - цветок;
- роза - (другой) цветок;
- красная и желтая розы - розы;
- лепесток является частью обоих видов цветов;
- пчелы опыляют цветы и питаются их нектаром.
Из этого простого примера следует, что классы, как и объекты, не существуют изолированно. В каждой проблемной области абстракции, описывающие ее, взаимодействуют различными способами.
Известны три основных типа отношений между классами. Во-первых, это отношение "обобщение/специализация" (общее и частное), т.е. иерархия "является". Розы являются цветами, что значит: розы являются специализированным частным случаем, подклассом более общего класса "цветы". Во-вторых, это отношение "целое/часть", т.е. иерархия "имеет". Например, лепестки являются частью цветов. В-третьих, это семантические, смысловые отношения, ассоциации. Например, пчелы ассоциируются с цветами.
Языки программирования выработали несколько общих подходов к выражению таких отношений. В частности, большинство объектно-ориентированных языков непосредственно поддерживает следующие виды отношений:
- ассоциация;
- агрегация;
- обобщение;
- зависимость;
- инстанцирование.
4.1 Ассоциация
Пример. Желая автоматизировать розничную торговую точку, мы обнаруживаем две абстракции - товары и продажи. Введем два класса: класс Product - это то, что мы продали в некоторой сделке, а класс Sale - сама сделка, в которой продано несколько товаров.
class Product; class Sale;
class Product {
Sale* lastSale;
. . .
};
class Sale {
Product** productSold;
. . .
};
Ассоциация - смысловая связь. По умолчанию, она не имеет направления и не объясняет, как классы общаются друг с другом. Мы можем только отметить семантическую зависимость, указав, какие роли играют классы друг для друга.
Так, ассоциация "Product - Sale" - двустороннее отношение: задавшись товаром, можно выйти на сделку, в которой он был продан, а пойдя от сделки, найти, что было продано.
Кратность (мощность) ассоциации - это количество ее участников. Различают три случая кратности ассоциации:
- один-к-одному;
- один-ко-многим;
- многие-ко-многим.
Рассмотренная в примере ассоциация имеет тип один-ко-многим: каждый экземпляр товара относится только к одной последней продаже, в то время как каждый экземпляр Sale может указывать на совокупность проданных товаров.
Отношение один-к-одному обозначает очень узкую ассоциацию. Например, в розничной системе продаж примером могла бы быть связь между классом "Продажа" и классом "Снятие денег с кредитной карточки": каждая продажа соответствует ровно одному снятию денег с данной кредитной карточки.
Отношение многие-ко-многим тоже нередки. Например, каждый объект класса "Покупатель" может инициировать сделку с несколькими объектами класса "Торговый агент", и каждый "Торговый агент" может взаимодействовать с несколькими объектами класса "Покупатель".
Класс может иметь ассоциацию с самим собой. Такая ассоциация называется рефлексивной.
4.2 Агрегация
Агрегация является частным случаем ассоциации. Отношение агрегации между классами имеет непосредственное отношение к агрегации между их экземплярами.
Пример. Вернемся к классу Controller, который является абстракцией объектов, управляющих температурой в теплице (см. п. 3.4).
Класс Controller - это целое, а экземпляр класса Heater (нагреватель) - одна из его частей. В рассмотренном случае мы имеем специальный случай агрегации - композицию.
Композиция - форма агрегирования, в которой целое владеет своими частями, имеющими одинаковое с ним время жизни. Части с нефиксированной кратностью могут быть созданы после создания агрегата, но, будучи созданными, живут и умирают вместе с ним. Такие части могут также быть явно удалены перед уничтожением агрегата. В случае композитного агрегирования объект в любой момент времени может быть частью только одного композита.
С точки зрения реализации в языке С++ композиция может быть осуществлена включением атрибута-части в класс-агрегат по значению, как это сделано в примере,приведенном в п. 3.4. Менее обязывающим является включение по ссылке. Мы могли бы изменить описание класса Controller:
Heater * h;
В этом случае класс Controller по-прежнему означает целое, но его часть, экземпляр класса Heater, содержится в целом косвенно. Теперь объекты живут отдельно друг от друга: мы можем создавать и уничтожать экземпляры классов независимо.
Агрегация является направленным отношением. Объект Heater входит в объект Controller, и не наоборот. Физическое вхождение одного в другое нельзя "зациклить", а указатели - можно (каждый из двух объектов может содержать указатель на другой).
4.3 Обобщение
4.3.1 Наследственная иерархия
Обобщение (наследование) - это такое отношение между классами, когда один класс повторяет структуру и поведение другого класса (одиночное наследование) или других классов (множественное наследование).
Пример. Рассмотрим определение типа Shape (фигура) для использования в графической системе. Предположим, у нас есть два класса:
class Point{ ... }; //Точка
class Со1ог{ ... }; //Цвет
Мы можем определить Shape следующим образом:
еnum Kind { circle, triangle, square }; // перечисление: окружность,
// треугольник, квадрат
class Shape {
Kind k, //поле типа (какая фигура?)
Point center, // центр фигуры
Color col, //цвет фигуры
public:
void move (Point to); // переместить
void draw ( ); // нарисовать
void rotate (int); // повернуть
Point isCenter ( );// возвращает значение центра фигуры
};
"Поле типа" k необходимо, чтобы такие операции, как draw и rotate, могли определить, с каким видом фигуры они имеют дело. Функцию draw ( ) можно определить следующим образом:
void Shape::draw ( ) {
switch (k) {
case circle: . . . break; // нарисовать окружность
case triangle: . . . break; // нарисовать треугольник
case square: . . . // нарисовать квадрат
}
}
Таким образом, функции должны "знать" обо всех возможных видах фигур. Поэтому код любой такой функции растет с добавлением новой фигуры в систему. Если мы определили новую фигуру, каждую операцию над фигурой нужно просмотреть и, вероятно, модифицировать. У нас есть возможность добавить новую фигуру к системе, только если мы имеем исходные тексты каждой функции. Так как добавление новой фигуры связано с внесением изменений в код каждой важной операции над фигурами, оно требует большого мастерства и потенциально влечет появление ошибок в коде, управляющем другими (старыми) фигурами.
Рассмотрим решение, использующее механизм наследования. Разделим общие свойства всех фигур (все они имеют цвет, их можно нарисовать и т.д.) и особые свойства фигур определенного типа (у окружности есть радиус, она вычерчивается специфическим способом и т.д.).
Сначала мы описываем класс, который определяет общие свойства всех фигур:
class Shape {
Point center;
Color col;
. . .
public:
void move (Point to) { center =to; /*...*/ draw ( ); };
virtual void draw ( ) = 0;
virtual void rotate (int angle)=0;
. . .
};
Описание virtual означает, что функция является виртуальной, т.е. может быть замещена в классе, производном от данного. Функция, интерфейс вызова которой может быть определен, а реализация - нет, объявляется чисто виртуальной, для чего используется синтаксис "= 0". Чисто виртуальная функция не имеет реализации в суперклассе и должна быть замещена в производном классе. Таким образом, виртуальные функции являются полиморфными.
Например, реализации функций draw и rotate могут быть определены только для конкретных фигур, поэтому эти функции вообще не реализованы в классе Shape. Чисто виртуальные функции впервые реализуются в производном классе.
Для описания полиморфизма используются два различных понятия: операция и метод. У класса есть операции, которые определяют его поведение, и методы - реализации данных операций. При этом каждый потомок класса может предоставить метод, реализующий любую унаследованную операцию, отличный от соответствующего метода предка. Чисто виртуальная функция (абстрактная операция) не имеет соответствующего метода.
Для определения конкретной фигуры мы должны сказать, что она является фигурой, и указать особые свойства (в том числе определить чисто виртуальные функции):
class Circle: public Shape {
int radius;
public:
void draw ( ){...};
void rotate (int) {};//функция ничего не делает
};
Класс Circle является производным от Shape, а класс Shape является базовым (родительским) для класса Circle. Альтернативная терминология называет Circle и Shape подклассом и суперклассом (или надклассом) соответственно.
Класс Circle создан для того, чтобы оперировать с его объектами. Классы, для которых создаются экземпляры, называют конкретными. С другой стороны, в классе Shape описаны чисто виртуальные функции, не имеющие реализации по определению, и создание экземпляров данного класса запрещено языком С++. Классы, экземпляры которых не создаются, называются абстрактными. Ожидается, что подклассы абстрактных классов доопределят их до жизнеспособной абстракции, наполняя класс содержанием. Классы, у которых нет потомков, называют листовыми.
Самый общий класс в иерархии классов называется базовым. В большинстве приложений базовых классов бывает несколько, и они отражают наиболее общие абстракции предметной области.
У класса обычно бывает два вида клиентов: экземпляры классов и подклассы. Часто полезно иметь для них разные интерфейсы. В частности, мы хотим показать только внешне видимое поведение для клиентов-экземпляров, но нам нужно открыть служебные функции и представления клиентам-подклассам.
Для этой цели описание класса разделяется на три части:
- открытую (public), видимую всем клиентам;
- защищенную (protected), видимую самому классу, его подклассам и друзьям (friend);
- закрытую (private), видимую только самому классу и его друзьям.
Таким образом, язык C++ позволяет достаточно гибко найти компромисс между принципами наследования и инкапсуляции.
Наследование подразумевает, что подклассы повторяют и могут дополнять структуры их суперклассов. В нашем примере класс Circle содержит элементы структуры суперкласса Shape и более специализированный элемент - радиус.
Поведение суперклассов также наследуется. Например, с помощью операции move класса Shape можно переместить экземпляр класса Circle. При этом в производном классе допускается добавление новых и переопределение существующих методов.
Например, опишем класс круг - SolidCircle - закрашенная окружность:
class SolidCircle: public Circle {
Color fillcol; // цвет заполнения
. . .
void draw ( ){ Circle:: draw ( ); ... };
};
Функция draw сначала вызывает аналогичную функцию родительского класса, которая рисует границу круга, а затем сама заполняет ее цветом.
В большинстве объектно-ориентированных языков программирования при реализации метода подкласса разрешается вызывать напрямую метод какого-либо суперкласса. Как видно из примера, это допускается и в том случае, если метод подкласса имеет такое же имя и фактически переопределяет метод суперкласса. В C++ для этого имя суперкласса добавляется в качестве префикса, тем самым формируется квалифицированное имя метода.
4.3.2 Обобщение и типизация
Вопросы построения наследственных иерархий тесно связаны с типизацией (в языках с сильной типизацией), поскольку при использовании наследования формируется и система типов.
При определении класса его суперкласс можно объявить public (как в нашем примере). В этом случае открытые и защищенные члены суперкласса становятся открытыми и защищенными членами подкласса. Таким образом, подкласс считается также и подтипом, то есть обязуется выполнять все обязательства суперкласса. В частности, он обеспечивает совместимое с суперклассом подмножество интерфейса и обладает неразличимым с точки зрения клиентов суперкласса поведением.
Если при определении класса объявить его суперкласс как private, это будет означать, что, наследуя структуру и поведение суперкласса, подкласс уже не будет его подтипом. Открытые и защищенные члены суперкласса станут закрытыми членами подкласса, и, следовательно, они будут недоступны подклассам более низкого уровня. В этом случае подкласс и суперкласс обладают несовместимыми интерфейсами с точки зрения клиента.
Если объявить суперкласс protected, то открытые и защищенные элементы такого суперкласса станут защищенными элементами подкласса. Однако, с точки зрения клиента интерфейсы класса и суперкласса несовместимы.
Пример. Продолжим рассмотрение наследственной иерархии, связанной с фигурами в графической системе. Сделаем следующие объявления:
Circle C1;
SolidCircle SC1, SC2;
Присвоение объекту А значения объекта В в языке С++ допустимо, если тип объекта В совпадает с типом объекта А или является его подтипом.
Поскольку SolidCircle является открытым подклассом Circle, следующий оператор присваивания правомочен:
C1 = SC1;
Хотя он формально и правилен, но опасен: любые дополнения в состоянии подкласса по сравнению с состоянием суперкласса срезаются. Таким образом, дополнительный атрибут fillcol, определенный в подклассе SolidCircle, будет потерян при копировании, поскольку его просто некуда записать в объекте класса Circle.
Следующий оператор недопустим:
SC2 = С1; // ошибка
Изменим описание класса SolidCircle на следующее:
class SolidCircle: Circle{. . .};
Теперь суперкласс Circle по умолчанию объявлен закрытым. Следовательно, его методы, например, move ( ), недоступны клиентам. Поскольку класс SolidCircle не является теперь подтипом Circle, мы уже не сможем присваивать экземпляры подкласса объектам суперкласса, как в случае объявления суперкласса в качестве открытого.
С1 = SC1; // теперь нельзя
Отметим, что унаследованной функции можно назначить тот же, что и в суперклассе, атрибут доступа в подклассе путем явной квалификации.
class SolidCircle: Circle{
public:
. . .
Circle:: move;
};
Правила C++ запрещают делать унаследованный элемент в подклассе "более открытым", чем в суперклассе. Например, член, объявленный в суперклассе защищенным, не может быть сделан в подклассе открытым путем явного упоминания.
С наследованием связан особый тип полиморфизма - включение (чистый полиморфизм). Данный тип полиморфизма реализуется при вызове виртуальных функций для указателей (ссылок) на объекты. При открытом наследовании указатель родительского класса может указывать на объекты всех подклассов. Если виртуальная функция имеет различные реализации в подклассах, то выбор, какую ее реализацию вызывать, определяется с учетом выяснения подтипа на этапе выполнения. То есть виртуальная функция вызывается в зависимости не от типа указателя, а от реального типа объекта, на который он указывает. Данная ситуация называется механизмом позднего связывания.
Пример. Опишем наследственную иерархию
class One {
public:virtual f ( ){return 1;}
};
class Two : public One {
public:virtual f ( ){return 2;}
};
Рассмотрим следующий фрагмент кода:
One one, *p;
Two two;
. . .
p = &one; p -> f ( ); // р указывает на объект типа One, f ( ) возвратит 1
p = &two; p -> f ( ); // р указывает на объект типа Two, f ( ) возвратит 2
one.f ( ); // f ( ) возвратит 1
one = two; one.f ( ); // one - объект типа One, f ( ) возвратит 1
С дугой стороны, если бы функция f ( ) не была виртуальной, то ее описание в обоих классах было бы не включением, а перегрузкой, и во всех случаях возвращалось бы значение 1.
Для безопасного выявления типа, на который направлен во время работы программы указатель родительского класса или ссылка на него, служит механизм динамического определения типа, специально введенный в язык С++.
Одним из инструментов данного механизма является оператор приведения dynamic_cast. Рассмотрим случай с указателем.
dynamic_cast < Тype* > (p);
Рассмотрим приведение потомка к типу родителя, которое называется повышающим приведением. Во время выполнения программы определяется реальный тип объекта, на который указывает указатель р. Если р типа Тype* или является указателем на открытый родительский класс для Тype, то результат будет точно такой же, как при простом присваивании р указателю типа Тype*. В противном случае возвращается нуль.
Пример. В предположении, что Circle - закрытый родительский класс класс для SolidCircle, рассмотрим следующий фрагмент кода:
Shape *S;
Circle *C;
SolidCircle *SС;
. . .
S = С; // правильно
S = dynamic_cast < Shape* > (С); // эквивалентно предыдущему
S = dynamic_cast < Shape* > (SС); // правильно, возвратится 0
S = SС; // ошибка
Приведение родителя к типу потомка называется понижающим приведением. Оно применяется только к классам, имеющим виртуальные функции. Если тип р - Тype* или его подтип, то оператор возвращает указатель на объект, иначе возвращается нуль.
Пример. Определим функцию, выясняющую, является ли объект, указатель на который служит ее аргументом, окружностью.
bool isCircle(Shape* ptr){
Circle* cptr = dynamic_cast < Circle*> (ptr);
return cptr != 0;
}
Использование функции иллюстрирует следующий фрагмент:
Circle C;
Triangle T;
. . .
isCircle(&C); // true
isCircle(&Т); // false
4.3.3 Множественное наследование
Рассмотрим две проблемы, которые возникают при множественном наследовании: конфликт имен между суперклассами и повторное наследование.
Конфликт имен происходит тогда, когда в двух или более суперклассах случайно оказывается элемент (переменная или операция) с одинаковым именем.
Пример. Определим абстракцию "Работающий студент". Для этого введем более общие абстракции "Работник" и "Студент". Абстракция "Работающий студент" будет наследовать компоненты обеих общих абстракций.
class Worker {
public:
intID_profession; // код профессии
char* Name; // имя
};
class Student {
public:
intID_university; // код университета
char* Name; // имя
};
class Student_Worker: public Student, public Worker { . . . };
Рассмотрим последовательность действий
Student_Worker He;
. . .
He.ID_profession; // правильно
He.Name; // неправильно - двусмысленно
Конфликт имен элементов подкласса может быть разрешен полной квалификацией имени члена класса, т.е. к именам добавляют префиксы, которые указывают имена тех классов, откуда они пришли.
He.Worker :: Name; // правильно
Повторное наследование возникает тогда, когда при задании более чем одного базового класса какой-либо класс дважды является базовым для другого класса.
Продолжим пример с работающим студентом. Анализируя глубже полученную иерархию наследования, мы обнаружим, что и работник, и студент имеют ряд общих признаков, в частности, имя. Разумно ввести еще более общую абстракцию "Человек".
class Person {
public:char* Name; // имя
}
class Worker : public Person {
public:int ID_profession; // код профессии
}
class Student : public Person {
public:int ID_university; // код университета
}
Наследственная иерархия класса Student_Worker представлена на рис. 4.1.
Рис. 4.1 Наследственная иерархия класса Student_Worker
Для доступа к одной из копий унаследованного элемента необходимо воспользоваться явной квалификацией, т.е. добавить к его имени префикс в виде имени класса-источника.
He.ID_profession; // правильно
He.Name; // неправильно - двусмысленно
He.Person :: Name; // неправильно - двусмысленно
He.Worker :: Name; // правильно
He.Student :: Name; // правильно
Продолжая анализ полученной иерархии, заметим, что работающий студент имеет всего одно имя. В результате объект класса Student_Worker должен использовать единственную копию элемента Name, унаследованную от Person. В результате приходим к ромбовидной структуре наследования для класса Student_Worker, представленной на рис 4.2.
Рис. 4.2 Ромбовидная структура наследования для класса Student_Worker
Одним из механизмов задания такого совместного использования является виртуальный базовый класс. Виртуальный базовый класс в производном классе представлен одним и тем же (совместно используемым) объектом. Для задания виртуального наследования используется синтаксис следующего примера.
class Person { . . .};
class Worker : public virtual Person {. . .};
class Student : public virtual Person {. . .};
class Student_Worker: public Student, public Worker {. . . };
4.4 Зависимость
Пример. Пусть управление температурой каждый объект класса Controller осуществляет в соответствии с задаваемым ему планом. План представим в виде экземпляра класса Plan.
class Plan;
class Controller{
. . .
void process (Plan& );
. . .
};
Класс Plan упомянут как часть описания функции-члена process; это дает нам основание сказать, что класс Controller пользуется услугами класса Plan.
Отношение зависимости (использования) между классами означает, что изменение в спецификации одного класса может повлиять на другой класс, который его использует, причем обратное в общем случае неверно. Можно сказать, что один из классов (клиент) пользуется услугами другого (сервера).
Один класс может использовать другой по-разному. В нашем примере это происходит при описании интерфейсной функции. Отношение использования также имеет место, если в реализации какой-либо операции происходит объявление локального объекта используемого класса.
4.5 Инстанцирование
Пример. Представим, что нам необходимы стек целых чисел и стек контроллеров, управляющих температурой. Мы могли бы описать два стека:
class IntStack {
int stack[100];
. . .
};
class ControllerStack {
Controller* stack[100];
. . .
};
Другой, более разумный, подход - создать универсальный стек, который мог бы хранить элементы любого нужного нам типа. Для этого мы можем описать стек, содержащий указатели на нетипизированные элементы:
class Stack {
void* stack[100];
. . . };
Однако это не безопасно с точки зрения типов. Никто не гарантирует нам, что пользователь не поместит в стек элемент одного типа, а взять захочет элемент другого типа.
Для реализации нашей идеи необходимо воспользоваться шаблоном или параметризованным классом. Шаблон служит для построения других классов и может быть параметризован другими классами, объектами или операциями.
Использование шаблонов реализует в языке С++ особый тип полиморфизма - параметрический полиморфизм.
template <class Тype> class Stack {
Тype stack[100];
. . .
public:
void push (Тype);
Т рор ( );
. . .
};
Префикс template < class Тype > делает Тype параметром объявления, которому этот префикс предшествует.
Инстанцирование - подстановка фактических параметров шаблона вместо формальных. В результате создается конкретный класс, который может иметь экземпляры.
Объявим нужные нам стеки:
typedef Stack < int > IntStack // синоним класса стеков целых чисел
typedef Stack < Controller* > ControllerStack // синоним класса стеков
// контроллеров
IntStack IS; // стек для целых чисел
ControllerStack CS; // стек для контроллеров
Объекты IS и CS - это экземпляры совершенно различных классов, которые даже не имеют общего суперкласса. Тем не менее они получены из одного параметризованного класса Stack.
Инстанцирование безопасно с точки зрения типов. По правилам C++ будет отвергнута любая попытка поместить в стек или извлечь из него что-либо, кроме целых чисел или указателей на экземпляры класса Controller, соответственно.
В языке С++ можно определять шаблоны не только классов, но и функций.
В качестве примера рассмотрим определение шаблона функции, служащей для определения максимального из двух элементов.
template <class Тype > Тype max(Тype x, Тype y){
return (x > y) ? x : y;
};
Теперь мы можем использовать один и тот же шаблон для целых и вещественных чисел.
int i, j, k;
double a, b, c;
. . .
k = max <int> (i, j);
c = max <double> (a, b);
Кроме того, возможно использовать этот шаблон и для объектов некоторого класса, если в нем определена операция ">".
4.6 Переменные и операции класса
Обычно для каждого объекта необходима своя копия переменных, описанных в классе. Однако в некоторых ситуациях требуется, чтобы в классе были данные, общие для всех его экземпляров - переменные класса.
Переменная класса в С++ описывается с ключевым словом static, она создается один раз как часть класса, а не для каждого конкретного экземпляра данного класса. Функция, которой требуется доступ к переменным класса, но не требуется, чтобы она вызывалась для конкретного экземпляра класса, также описывается как статическая (static).
Пример. Опишем класс для работы с датами.
class Date{
int day;// день
int month;// месяц
int year;// год
public:
Date (int, int, int); // день, месяц, год
Date (int, int); // день, месяц, текущий год - по умолчанию
Date (int); // день, текущие месяц и год - по умолчанию
Date ( ); // дата по умолчанию - сегодня
. . .
};
В данном примере мы объявили четыре конструктора для того, чтобы иметь возможность задавать дату в сокращенном виде. В крупных проектах количество конструкторов, позволяющее учесть различные варианты использования, может быть намного больше.
Рассмотрим альтернативное решение. Опишем конструктор с аргументами по умолчанию.
class Date{
int day, month, year;
static Date default_date;
public:
Date (int d=0, int m=0, int y=0);
. . .
static void set_default(int, int, int);
};
Если значение аргумента является нулевым, следует воспользоваться соответствующим элементом даты, задаваемой по умолчанию - default_date. Поскольку значение default_date должно быть одинаковым для всех объектов класса, данная переменная является статическим членом. Для ее инициализации описана также статическая функция-член set_default. Статические члены - и функции, и данные - должны быть где-то определены. Например,
Date Date :: default_date (1, 1, 2000);
void set_default (int d, int m, int y)
{
Date :: default_date= Date (d, m, y);
}
Статические члены используются также для реализации на языке С++ утилит. Утилитами называют совокупность глобальных переменных и свободных подпрограмм, сгруппированных в форме объявления класса. В этом случае глобальные переменные и свободные подпрограммы рассматриваются как члены класса, причем именно как статические. Введение утилит позволяет приблизить реализацию системы на языке С++ к набору классов и взаимодействующих объектов, как в чисто объектно-ориентированных языках.
4.7 Интерфейсы
Когда с помощью объектно-ориентированного подхода начали разрабатывать крупные программные системы, выяснилось, что кроме классов нужны дополнительные уровни абстракции. В частности, если сложный объект имеет разное поведение, в зависимости от того, с кем он взаимодействует, то бывает удобно скрыть все функции, не нужные в данный момент. А точнее: на время данного взаимодействия сделать доступными все необходимые функции и только их. Для описания таких групп функций удобно использовать понятие интерфейса. В данном контексте интерфейс удобно рассматривать как абстрактный класс, не имеющий собственных данных.
Пример. Все элементы управления телевизора можно разделить на несколько групп: пользовательские (громкость, номер канала), специальные (частота канала) и аппаратные (параметры электрических цепей). При этом пользователь работает с пользовательскими органами управления, настройщик - со специальными, а телемастер - с аппаратными. При этом, если телевизор исправен и настроен, пользователю нет необходимости видеть и менять состояние специальных и аппаратных органов управления. Поэтому пользовательские элементы управления обычно выносятся на переднюю панель телевизора, специальные закрыты небольшой дверцей, а аппаратные вообще погружены внутрь корпуса. Если бы все было на поверхности, пользователь мог бы сделать все то же, что и раньше, но для него оказались бы доступными специальные и аппаратные органы управления, и он мог бы случайно испортить настройки. Кроме того, передняя панель была бы загромождена настолько, что мало кто смог бы ориентироваться в обилии кнопок, ручек и т.п.
Между интерфейсами могут существовать отношения обобщения, ассоциации и зависимости, аналогичные одноименным отношениям между классами. Между классом и интерфейсом могут существовать отношения реализации и зависимости. Будем говорить, что класс реализует (или поддерживает) интерфейс, если он содержит методы, реализующие все операции интерфейса. Интерфейс может реализовываться несколькими классами, а класс может реализовывать несколько интерфейсов. С другой стороны, класс может зависеть от нескольких интерфейсов, при этом предполагается, что какие-то классы эти интерфейсы реализуют.
На рис. 4.3-4.4 показано, что пользователь взаимодействует с телевизором посредством интерфейса IUser, а телемастер - посредством интерфейса IApparatus.
Рис. 4.3. Интерфейс пользователя
Рис. 4.4. Интерфейс телемастера
4.8 Группирование классов
Когда система разрастается до десятка классов, можно заметить группы классов, связанные внутри и слабо зацепляющиеся с другими. Такие группы классов образуют пакет. Пакетом в области объектно-ориентированных технологий называют общий механизм организации элементов в группы. В данном контексте мы будем говорить только о группировании классов и называть пакетом группы, содержащие классы и другие пакеты.
Пакет не имеет операций или состояний в явном виде, они содержатся в нем неявно в описаниях агрегированных классов.
Некоторые классы в пакете могут быть открытыми, то есть экспортироваться для использования за пределы пакета. Остальные классы могут быть частью реализации, то есть не использоваться никакими классами, внешними к этому пакету.
В C++ пакеты классов реализуются с помощью введения пространств имен - namespace. Однако пространство имен позволяет реализовать более широкое понятие. Оно может включать в себя классы, другие пространства имен, свободные подпрограммы и глобальные (внутри пространства имен) данные.
Пример. Объединим все классы, разработанные для использования в графической системе в одну компоненту. Предоставим пользователю описания в файле GraphSys.h:
namespace GraphSys{
class Point{ ... };
class Color{ ... };
class Shape {... };
class Circle: public Shape{... };
class Triangle: public Shape {... };
class Square: public Shape{... };
class SolidCircle: public Circle {... };
}
Реализация указанных классов находится в файле GraphSys.срр:
namespace GraphSys{
Circle :: draw ( ){...}
Triangle :: draw ( ){...}
Square :: draw ( ){...}
SolidCircle :: draw ( ){...}
. . .
}
Обращение к членам пространства имен осуществляется с использованием явной квалификации:
GraphSys :: Circle C;
GraphSys :: SolidCircle SC;
С другой стороны, описание using в пользовательском коде позволяет не использовать все время явную квалификацию:
#include GraphSys.h
using namespace GraphSys;
void user_func ( ){
Circle C;
. . .
C -> draw ( );
}
5. Объектно-ориентированный анализ
Одной из основных задач, решаемых на этапе анализа и ранних стадиях проектирования, является выявление ключевых абстракций задачи - классов и объектов, составляющих словарь предметной области.
На ранних стадиях внимание проектировщика сосредоточивается на внешних проявлениях ключевых абстракций. Такой подход создает логический каркас системы: структуры классов и объектов. На последующих фазах внимание переключается на внутреннее поведение ключевых абстракций, а также их физическое представление.
Выделим следующие способы проведения объектно-ориентированного анализа:
- классический подход;
- анализ поведения;
- анализ предметной области;
- анализ вариантов;
- CRC-карточки;
- неформальное описание.
Классический подход к классификации предполагает, что все вещи, обладающие некоторым свойством или совокупностью свойств, формируют некоторую категорию. Причем наличие этих свойств является необходимым и достаточным условием, определяющим категорию.
Например, студент - это категория: каждый человек или является студентом, или не является, и этого признака достаточно для решения вопроса, к какой категории принадлежит тот или иной индивидуум. С другой стороны, высокие люди не определяют категории, если, конечно, мы специально не уточним критерий, позволяющий четко отличать высоких людей от невысоких.
Таким образом, классический подход в качестве критерия похожести объектов использует родственность их свойств. В частности, объекты можно разбивать на непересекающиеся множества в зависимости от наличия или отсутствия некоторого признака.
Какие конкретно свойства надо принимать во внимание? Это зависит от цели классификации. Например, цвет автомобиля надо зафиксировать в задаче учета продукции автозавода, но он не интересен программе, которая управляет уличным светофором. Поэтому нет абсолютного критерия классификации, одна и та же структура классов может подходить для одной задачи и не годиться для другой.
При данном подходе кандидатами в классы и объекты могут быть выбраны:
- осязаемые предметы (автомобили, датчики);
- роли (учитель, политик);
- события (посадка на Марс, запрос);
- взаимодействие (заем, встреча).
Анализ поведения сосредоточивается на динамическом поведении как на первоисточнике объектов и классов. Классы формируются на основе групп объектов, демонстрирующих сходное поведение.
Напомним, что ответственностью объекта называют совокупность всех услуг, которые он может предоставлять по всем его контрактам. В результате анализа объединяют объекты, имеющие сходные ответственности и строят иерархию классов, в которую каждый подкласс, выполняя обязательства суперкласса, привносит свои дополнительные услуги.
До сих пор мы неявно имели в виду единственное разрабатываемое нами приложение. Но иногда в поисках полезных и уже доказавших свою работоспособность идей полезно обратиться сразу ко всем приложениям в рамках данной предметной области. Анализ данной предметной области может указать на ключевые абстракции, оказавшиеся полезными в сходных системах. Анализ предметной области - это попытка выделить те объекты, операции и связи, которые эксперты данной области считают наиболее важными.
Анализ включает следующие этапы:
- построение скелетной модели предметной области при консультациях с экспертами в этой области;
- изучение существующих в данной области систем и представление результатов в стандартном виде;
- определение сходства и различий между системами при участии экспертов;
- уточнение общей модели для приспособления к нуждам конкретной системы.
Анализ области можно вести относительно аналогичных приложений (вертикально) или относительно аналогичных частей одного и того же приложения (горизонтально).
...Подобные документы
Общая характеристика объектно-ориентированного подхода в программировании, его основные свойства и принципы. Разработка программы для автоматизация деятельности кафе на основе объектно-ориентированного подхода, проектирования и реализации схемы данных.
курсовая работа [1,2 M], добавлен 22.01.2012Анализ объектно-ориентированного программирования, имитирующего способы выполнения предметов. Основные принципы объектно-ориентированного программирования: инкапсуляция, наследование, полиморфизм. Понятие классов, полей, методов, сообщений, событий.
контрольная работа [51,7 K], добавлен 22.01.2013Понятие объектно-ориентированного программирования, характеристика используемых языков. Практическая разработка средств объектно-ориентированного программирования в задачах защиты информации: программная реализация на языке С++, а также Turbo Pascal.
курсовая работа [275,9 K], добавлен 22.12.2011Использование объектно-ориентированного программирования - хорошее решение при разработке крупных программных проектов. Объект и класс как основа объектно-ориентированного языка. Понятие объектно-ориентированных языков. Языки и программное окружение.
контрольная работа [60,1 K], добавлен 17.01.2011Основная цель технологии СОМ (объектная модель компонентов) - обеспечение возможности экспорта объектов. Объектно-ориентированное программирование и его место в программировании. Принципы и применение описаний информационных систем (UML и аналоги).
курсовая работа [698,3 K], добавлен 09.12.2013Использование скриптового языка программирования для разработки web-приложений (сценариев). Изучение основ объектно-ориентированного программирования в языке PHP. Ознакомление со специальными методами для работы с классами. Назначение интерфейсов.
контрольная работа [25,1 K], добавлен 14.03.2015Технологии программирования. Сущность объектно-ориентированного подхода к программированию. Назначение Си, исторические сведения. Алфавит, базовые типы и описание данных. Структуры и объединения. Операторы Си++. Функции. Библиотека времени выполнения.
курс лекций [51,9 K], добавлен 03.10.2008Приемы и правила объектно-ориентированного программирования с использованием языка С++. Общие принципы разработки объектно-ориентированных программ. Основные конструкции языка С++. Разработка различных программ для Windows с использованием WIN32 API.
учебное пособие [1,6 M], добавлен 28.12.2013Разработка программы с использованием принципов объектно-ориентированного программирования на языке высокого уровня С средствами Microsoft Visual Studio 2010. Построение алгоритма реализации. Класс программы, инструкция по использованию программы.
курсовая работа [1,0 M], добавлен 26.12.2013Характеристики и свойства языков программирования. Исследование эволюции объектно-ориентированных языков программирования. Построение эволюционной карты механизмов ООП. Разработка концептуальной модели функционирования пользовательского интерфейса.
курсовая работа [2,6 M], добавлен 17.11.2014Основные элементы объектной модели. Сущность и преимущества объектно-ориентированного подхода, понятие объекта и класса. Унифицированный язык моделирования UML. Диаграммы классов и взаимодействия: назначение, построение и примеры использования.
реферат [273,2 K], добавлен 09.06.2009Анализ проблематики построения объектно-ориентированного канала связи. Основные понятия протокола Modbus. Возможности CodeSys для реализации объектно-ориентированного подхода. Разработка методики кроссплатформенной библиотеки для интеграции устройств.
курсовая работа [38,6 K], добавлен 15.06.2013Особенности объектно-ориентированного проектирования. Основные понятия объектно-ориентированного подхода. Основы языка UML, варианты его использования. Диаграммы классов и взаимодействия. Разработка диаграммы прецедентов (вариантов использования).
курсовая работа [1,1 M], добавлен 13.05.2014Исследование принципов объектно-ориентированного программирования на базе языка программирования С++. Разработка программного комплекса для ведения учёта памятников города. Описание процессов сортировки, поиска, формирования статистики по памятникам.
курсовая работа [782,4 K], добавлен 26.05.2014Изучение принципов объектно-ориентированного программирования, в котором основными концепциями являются понятия классов и объектов. Свойства этого вида программирования: инкапсуляция, полиморфизм, наследование. Описание класса. Конструкторы и деструкторы.
презентация [74,8 K], добавлен 14.10.2013Понятие объектно-ориентированного программирования, общая характеристика языков высокого уровня. Разработка программного обеспечения для реализации компьютерной игры "пинбол" с помощью императивного программирования в среде Microsoft Visual Basic.
курсовая работа [428,9 K], добавлен 19.09.2012Создание программного обеспечения - системы имитационного моделирования на тему "Производственная линия с пунктами технического контроля". Описание входных и выходных данных. Объектно-ориентированное программирование. Диаграммы модулей и процессов.
курсовая работа [1,2 M], добавлен 09.01.2014История развития, характеристика, предназначение и особенности языка программирования Delphi. Разработка проекта создания кроссворда на объектно-ориентированном языке Delphi, который состоит из 19 вопросов по горизонтали и 16 вопросов по вертикали.
курсовая работа [970,5 K], добавлен 15.05.2014Свойства объектно-ориентированного языка программирования. Понятия инкапсуляции и наследования. Виртуальные функции и полиморфизм. Инициализация экземпляра объекта с помощью конструктора. Динамическое создание объектов. Совместимость объектных типов.
реферат [17,0 K], добавлен 15.04.2015Методология объектно-ориентированного программирования в Java. Понятия класса, объекта и объектной переменной. Динамическая и статическая объектные модели. Логическое структурирование приложения. Наследование в Java. Отличия интерфейсов от классов.
курс лекций [547,2 K], добавлен 01.05.2014