Курс лекций по мдк 01.02 Прикладное программирование специальности 09.02.03
методическая разработка на тему

 

 

Скачать:

ВложениеРазмер
Файл lektsii_pm01.docx351.04 КБ

Предварительный просмотр:

Министерство образования и науки Краснодарского края

ГБОУ СПО «АМТ» КК

Курс лекций

МДК 01.02 «Прикладное программирование»

ПМ 01 «Разработка программных модулей программного обеспечения для компьютерных систем»

для специальности

09.02.03 «Программирование в компьютерных системах»

2014


2

3

Содержание

Введение……………………………………………………………………

4

Принципы технологии объектно-ориентированного

5

программирования…………………………………………………………

Абстрактные типы данных в С++…………………………………………

8

Классы С++ и абстрактные типы данных. Класс как расширенное

11

понятие структуры…………………………………………………………

Инициализация и разрушение объектов………………………………….

14

Спецификация класса. Построение диаграммы класса для объекта…...

16

Создание объектов на основе классов Объекты и композиция(стр5)

19

Наследование классов. Множественное и одиночное наследование.

21

Инкапсуляция, производный класс……………………………………….

Доступ к членам базовых классов. Определение базового класса.

22

Определение производного класса………………………………………..

Статические поля класса…………………………………………………...

24

Друзья класса. Использование служебного слова friend………………...

25

Перегрузка операций (операторов) для классов. Функции-операции.

28

Бинарные операции. Унарные операции………………………………….

Полиморфизм и виртуальные функции…………………………………...

29

Области действия и пространства имён………………………...………...

31

Обработка исключительных ситуаций.

33

Классы коллекций. Линейные коллекции. Коллекции с прямым

доступом. Нелинейные коллекции………………………………………..

36

Создание класса для работы со строками………………………………...

38

Создание класса «дерево»………………………………………………….

40

Создание класса « стек»………………………………………………........

42

Создание класса «очередь»………………………………………………...

44

Литература

46


4

Введение

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

ориентированного программирования» МДК 01.02 «Прикладное программирование» ПМ 01. «Разработка программных модулей программного обеспечения для компьютерных систем», для специальности 230115 «Программирование в компьютерных системах».

Цели и задачи курса лекций:

  • изучение  объектно-ориентированного  языка  программирования  Visual

С++;

  • приобретение практических навыков программирования задач и отладки программ на языке Visual С++.

Рассматриваемый курс содержит: 19 тем; список литературы.

Раздел        «Реализация        программных        модулей        с        использованием

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

разработкой        современных        приложений        в        различных        отраслях

промышленности.

Целью        преподавания        раздела        МДК        01.02        является        освоение

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

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


5

Тема «Принципы технологии объектно-ориентированного

программирования»

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

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

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

Рассмотрим наиболее известные из технологий:

  • метод "северо-западного" угла (имеется в виду лист бумаги или экран дисплея). Программа пишется сразу от начала до конца, без использования каких-либо общих принципов;

  • технология структурного программирования, в ней предполагается придерживаться принципов модульности, нисходящего и пошагового проектирования программ, одновременного проектирования программ и структур данных.

  • технология объектного программирования: связана с использованием при проектировании программы понятий объектов и их классов.

Что первично: алгоритм (процедура, функция) или обрабатываемые им данные? В традиционной технологии программирования взаимоотношения процедуры - данные имеют более-менее свободный характер, причем процедуры (функции) являются ведущими в этой связке: как правило, функция вызывает функцию, передавая данные друг - другу по цепочке. Соответственно, технология структурного проектирования программ прежде всего уделяет внимание разработке алгоритма.

  • технологии ООП взаимоотношения данных и алгоритма имеют более регулярный характер: во-первых, класс (базовое понятие этой технологии) объединяет в себе данные (структурированная переменная) и методы


6

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

.

Модульное программирование.

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

  • размер модуля должен быть ограничен;

  • модуль должен выполнять логически целостное и завершенное действие;

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

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

Еще одной, но уже физической единицей программы является текстовый файл, содержащий некоторое количество функций и определений типов данных и переменных. Модульное программирование на уровне файлов - это возможность разделить полный текст программы на несколько файлов, транслировать их независимо друг от друга.

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

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

Нисходящее программирование.

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


7

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

  • простой последовательности действий;
  • конструкции выбора или оператора if;

  • конструкции повторения или цикла.

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

  • первоначально программа формулируется в виде некоторого неформального действия на естественном языке;

  • первоначально определяются входные параметры и результат действия;

  • очередной шаг детализации не меняет структуру программы, полученную на предыдущих шагах;

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

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

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

Пошаговое программирование.

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

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

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

Структурное программирование.

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


8

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

Структурное программирование - модульное нисходящее пошаговое проектирование алгоритма и структур данных.

Тема «Абстрактные типы данных в С++ (ADT)»

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

Пример

struct student {

int id;

char name [20]; };

struct student s= { 567, “Лопухин Олег”}

Что определяет тип? Тип определяет два типа информации: набор свойств и набор операций. Предположим, что нужно определить новый тип данных, для этого:

  1. необходимо обеспечить способ хранения данных, возможно разработав структуру.
  2. необходимо обеспечить способы манипулирования данными.

  • информатике был разработан успешный способ определения новых типов данных. Это трехэтапный процесс перехода от абстрактного понятия к конкретной реализации:

  • Создайте абстрактное описание свойств типа и операций, которые можно выполнять с данным типом. Это описание не должно быть связано с конкретной реализацией. Оно даже не должно быть связано с конкретным языком программирования. Такое формальное абстрактное описание называется абстрактным типом данных(ADT).

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


9

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

Рассмотримпрактическийслучайпредставленияданных.

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

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

#define        TSIZE        45        /*        размер        массива,

предназначенного для хранения заголовка фильма*/ struct film{

char title [TSIZE];

int rating;

struct film *next;

};

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

Прежде чем рассматривать текст программы на языке С для связанного списка, рассмотрим такой список с концептуальной точки зрения. Предположим, что пользователь вводит Modern Times в качестве заголовка и 10 - в качестве рейтинга. Программа выделит память для структуры film , скопирует строку Modern Times в элемент title и установит значение элемента rating равным 10. Для указания того, что никакая структура не следует за текущей, программа установит указатель поля next в NULL (нулевой указатель). Конечно, необходимо отслеживать место хранения первой структуры. Это можно сделать, присвоив ее адрес отдельному указателю, который мы будем называть заглавным указателем. Заглавный указатель указывает на первый элемент в связанном списке элементов. Вид этой структуры показан на рисунке.

struct film *head;

2240

2240

Modern Times

10

NULL


10

head        название        рейтинг next

Рис. 1 Первый элемент в связанном списке.

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

2240

2240

Modern Times

10

2360

head

название

рейтинг

next

2360

Titanic

12

NULL

название

рейтинг

next

Рис. 2 Связанный список с двумя элементами.

Каждый  новый  фильм

будет  отображаться  таким  же  образом.  Его

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

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

Все что требуется для проекта описания фильмов - это список элементов. Каждый элемент содержит название фильма и рейтинг. Необходимо иметь возможность добавлять новые элементы к концу списка и отображать его содержимое. Назовем абстрактный тип, который будет выполнять эти задачи, list. Какими свойствами должен обладать список? Ясно, что он должен быть способным содержать последовательность элементов. Иначе говоря, список может содержать несколько элементов, и эти элементы должны каким-то образом быть упорядочены, чтобы можно было говорить о первом элементе в списке, о втором или о последнем. Далее, тип списка должен поддерживать такие операции, как добавление элемента в список.

Ниже приведены некоторые операции:


11

  • Инициализация пустого списка.
  • Добавление элемента в конец списка.
  • Определение того, является ли список пустым.
  • Определение того, является ли список заполненным.
  • Определение количества элементов в списке.

  • Обращение к каждому элементу списка для выполнения какого-либо действия.
  • Вставка элемента в любое место списка.
  • Удаление элемента из списка.
  • Замена одного элемента списка другим.

  • Поиск элемента в списке

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

Вот как выглядит краткое описание типа:

Имя типа:        Simple List ( Простой список )

Свойства типа:        Может содержать последовательность элементов.

Операции с типом:        Инициализация пустого списка. Определение того, является ли список пустым.

Определение того, является ли список

заполненным.

Определение количества элементов в списке. Добавление элемента в конец списка. Перемещение по списку с обработкой каждого элемента списка.

Тема «Классы С++ и абстрактные типы данных. Класс как расширенное понятие структуры»

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


12

обеспечить представления предметной области решаемой задачи. В языке С++ можно вводить собственные типы данных и определять операции над ними с помощью классов.

Рассмотрим отличие структуры от класса. Структура – это объединяемое в единое целое множество поименованных элементов в общем случае разных типов.

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

Создадим вначале структуру «человек - примитивный». Структура содержит только данные ( параметры):

Позиция_х = 3

Позиция_х =4

Движется = false

Сила_правой_руки =10

Сила_левой_руки = 6

Правая_рука_занята=true

Правая_рука_занята=true

Дальнозоркость = 90

Жизненная сила =100

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

Функциональные возможности:

Осмотреться();

Позиция_свободна(x,y);

Передвинуться_на_позицию(x,y);

Идти_вперёд();

Идти_назад();

Идти_влево();

Идти_вправо();

Остановиться();

Проверить_заняты_ли_руки();

Взять_предмет(какой);

Если к данным добавить функциональные возможности, то получиться класс «человек - примитивный»

Переменная типа «класс» называется объектом (object). Класс создает различные уровни доступа к его полям, разделяя объявление на части: private, protected и public. Часть private (закрытая) объекта может быть доступна только для функций-полей в этом классе. Часть public (открытая) объекта может быть доступна для внешних элементов программы , в области действия которых находится этот объект (рис. 3.1). Protected (защищенные) члены используются с производными классами.


13

Private:

Данные

методы

Поля класса

Public:

Внешние программные единицы

Данные

методы

Рис 3 Доступ к методам класса

Объявление класса

Объявление класса начинается с заголовка класса (class head), состоящего из зарезервированного слова class, за которым следует имя класса. Поля . класса определяются в теле класса (class body), которое заключается в фигурные скобки и заканчивается точкой с запятой. Зарезервированные слова public и private разделяют поля класса, и эти спецификаторы доступа заканчиваются двоеточием. Поля класса объявляются как переменные С++, а методы задаются, как объявления функций С++. Общая форма объявления класса такова: '

class Имя класса

{

private:

  • Закрытые данные
  • Объявление закрытых методов
  • . . . . . . . .

pиblic:

  • Открытые данные

  • Объявление открытых методов

  • . . . . . . . .

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

Пример

Класс Rectangle

При геометрических измерениях прямоугольник определяется его длиной и шириной. Это позволяет нам вычислять периметр и площадь фигуры. Параметры длины и ширины и операции объединяются для образования абстрактного типа данных прямоугольной фигуры. Мы разрабатываем класс Rectangle. Класс содержит конструктор и набор методов - GetLength, PutLength, GetWidth и PutWidth, имеющих доступ к закрытым полям класса. Объявление класса Rectangle следующее:

class Rectang1e


14

{

private:

//длина и ширина прямоугольного объекта f1oat 1ength, width;

pиblic:

  • конструктор

Rectang1e(f1oat = 0, f1oat = 0);

  • методы для нахождения и изменения закрытых данных float GetLength(void) const;

void PutLength(f1oat 1); float GetWidth(void) const; void PutWidth{f1oat w);

  • вычислять и возвращать измерения прямоугольника float Perimeter (void) const;

float Area(void) const;

};

Обратите внимание, что методы GetLength, GetWidth, Perimeter и Area имеют ключевое слово const после списка параметров. Это объявляет каждый метод как константный. В определении константного метода, никакой элемент данных не может быть изменен. Другими словами, выполнение метода, объявленного как const, не изменяет состояния объекта Rectangle.

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

Тема «Инициализация и разрушение объектов»

Конструктор

Функция, называемая конструктором (constructor) класса, имеет то же имя, что и класс. Подобно другим функциям С++, конструктору могут передаваться параметры, используемые для инициализации одного или более данных-полей класса. В классе Rectangle конструктору дается имя Rectangle, и он принимает параметры l и w, используемые для инициализации длины и ширины объекта, соответственно. Заметьте, что эти параметры имеют значения по умолчанию, которые указывают, что используется значение 0, когда параметр 1 или w не передается явно.

Деструктор

Динамическое выделение памяти для объектов какого – либо класса создаёт необходимость в освобождении этой памяти при уничтожении объекта. Такую возможность даёт специальный метод класса – деструктор. Так если в конструкторе класса «матрица» выделяется память для 10


15

целочисленных элементов, то в деструкторе эта память должна освобождаться.

Операторы конструктора:

int *m;

m= new int [i*j];

Операторы деструктора:

delete m;

Для деструктора предусмотрен специальный формат:

~имя_класса () { операторы деструктора}

Конструкторы и деструкторы особые члены класса, служащие для инициализации и уничтожения объекта. Ниже приведены некоторые их особенности:

  • они не возвращают никакого значения даже void;
  • не учавствуют в механизме наследования;
  • нельзя получить их адрес;
  • конструкторы не могут быть виртуальными;

  • деструктор не имеет аргументов, а значит его нельзя перегружать;

  • автоматически вызываются при создании и уничтожении

объекта.

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

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

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

Объявление объекта

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

ClassName object («parameters»); //список параметров может быть пустым


спецификации класса.

16

Например, следующие объявления создают два объекта типа Rectangle:

Rectangle room(12, 10);

Rectangle t; //использование параметров по умолчанию (0, 0). Каждый объект имеет полный диапазон данных и методов,

объявляемых в классе. Открытые поля доступны с использованием имени объекта и имени поля, разделяемых "." (точкой). Например:

  • = room.Area();// присваивает х площадь = 12 * 10 = 120

t.PиtLength(20); // присваивает 20 как длину объекта Rectangle

  • Текущая длина равна 0, так как используются
  • параметры по умолчанию.

cout << t.GetWidth();// выводит текущую ширину, которая = 0 по умолчанию

  • объявлении объекта Room конструктор первоначально устанавливает значение длины, равным 12, а ширины - 10. Клиент может изменять размеры, используя методы доступа PutLength и PutWidth:

room.PutLength(15); // изменение длины и ширины на 15 и 12 room.PutWidth(12);

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

Класс Rectangle содержит поля - данные класса типа float. В общем, класс может содержать элементы любого допустимого типа С++, даже других классов. Однако, класс не может содержать объект его собственного типа в качестве поля.

Тема «Спецификация класса. Построение диаграммы класса для объекта»

Спецификация класса предоставляет пользовательский интерфейс класса и содержит всю информацию о классе для пользователя. Специфика-ция класса также содержит информацию для транслятора о назначении класса объектов.

Тело класса содержит тела функций, описанных в


17

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

Диаграммы классов:

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

  • На них отображают классы, категории классов и отношения между ними.

  • Возможность отражения параметризованых классов.

  • Допускаются разнообразные отношения: ассоциации, включения, использования, наследования.

  • Имеются простые и удобные средства просмотра иерархий диаграмм классов.

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

Рис. 4. Графическое обозначение класса

Схема классов содержит их простое перечисление, с обозначением того

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

Рис. 5. Схема классов объектов

Схема атрибутов:

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


18

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

организация: ( наименование, адрес, руководитель,телефон )

владелец: ( имя, адрес, телефон )

событие: ( дата, время )

регистрация: ( дата, время, начало, конец )

Схема методов

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

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

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

Диаграмма классов - это набор статических, декларативных элементов модели. Диаграммы классов могут применяться и при прямом проектировании, то есть в процессе разработки новой системы, и при обратном проектировании - описании существующих и используемых систем. Информация с диаграммы классов напрямую отображается в исходный код приложения - в большинстве существующих инструментов UML-моделирования возможна кодогенерация для определенного языка программирования (обычно Java или C++). Таким образом, диаграмма классов - конечный результат проектирования и отправная точка процесса разработки.

Рис. 6. Диаграмма классов для "генеалогического древа" бытовой техники.


19

Рис. 7. Диаграмма классов образовательного учреждения


20

Тема «Создание объектов на основе классов. Объекты и композиция»

Центральными в ООП являются понятия класса и объекта. Образно говоря, ООП заключается не столько в использовании классов и объектов в программе, сколько в замене принципа программирования "от функции к функции" принципом программирования "от класса к классу".

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

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

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

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

Это определение можно проиллюстрировать средствами классического

Си:

struct myclass

{

int data1;

...

};

void method1(struct myclass *this,...)

{ ... this->data1 ... }

void method2(struct myclass *this,...)

{ ... this->data1 ... } struct myclass obj1, obj2;

... method1(&obj1,...); ... method2(&obj2,...);


21

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

- класс определяется как структурированный тип данных (struct); - объекты определяются как переменные класса;

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

struct matrix

{

  • определение структурированного типа matrix и методов,

  • реализующих операции matrix * matrix, matrix * double

}

matrix a,b;        // Определение переменных и

double dd;        // объектов класса matrix

a = a * b; // Использование переопределенных b = b * dd * 5.0; // операций

Класс - определенный программистом базовый тип данных.

Объект - переменная класса.

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

а) объект не может иметь экземпляров (имеет 0 экземпляров)

б) объект может иметь только один экземпляр в рамках данной задачи в) объект может иметь много экземпляров

Примером отношений являются:

а) объект млекопитающее не может иметь экземпляров, т.к. любое реальное млекопитающее является экземпляром объекта-потомка от млекопитающего.

б) объект "президент России" может иметь только один экземпляр в течение некоторого периода времени.

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

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

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


22

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

Тема «Наследование классов. Множественное и одиночное наследование. Инкапсуляция, производный класс»

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

Для этого в структуре class используется спецификатор раздела private, содержащий данные и методы, доступные только для самого класса. Если данные и методы содержатся в разделе public, они доступны извне класса. Раздел protected содержит данные и методы, доступные из класса и любого его производного класса. Наличие последних позволяет говорить об иерархии классов, где есть классы - родители - шаблоны для создания классов - потомков. Объекты, полученные из описания класса, называют экземплярами этого класса.

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

Иначе говоря, новый класс наследует как данные старого класса, так

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

Наследование свойств в объектно-ориентированном подходе понимается как наследование атрибутов и методов, т.е. возможность использования в производном объекте атрибутов и методов базового объекта. Пусть имеется два объекта:

объект: геометрическая фигура атрибуты:

позиция на плоскости методы:

создать

удалить

переместить в новую позицию


23

отобразить

стереть

/////////////////////////////////////

объект: прямоугольник

атрибуты:

высота

ширина

методы:

создать

отобразить

Пусть объект прямоугольник является наследником объекта геометрическая фигура. Тогда прямоугольник имеет три атрибута: позиция, высота, ширина и пять методов поведения. Среди них атрибут позиция наследуется от объекта «геометрическая фигура», а атрибуты высота и ширина - собственные.

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

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

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

Тема «Доступ к членам базовых классов. Определение базового класса. Определение производного класса»

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


24

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

Допускается множественное наследование – возможность для некоторого класса наследовать компоненты нескольких никак не связанных между собой базовых классов. В иерархии классов соглашение относительно доступности компонентов класса следующие:

  • private   Член  класса  может  использоваться  только  функциями-

членами данного класса и функциями-“друзьями” своего класса. В производном классе он недоступен.

  • protected  То же, что и private, но дополнительно член класса с

данным атрибутом доступа может использоваться функциями-членами и функциями-“друзьями” классов, производных от данного.

  • public    Член  класса  может  использоваться  любой  функцией,

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

Следует иметь в виду, что объявление friend не является атрибутом доступа и не наследуется.

Синтаксис определение производного класса:

class имя_класса : список_базовых_классов {список_компонентов_класса};

  • производном классе унаследованные компоненты получают статус доступа private, если новый класс определен с помощью ключевого слова class, и статус public, если с помощью struct.

Например,

а) class S : X, Y, Z {...}; S – производный класс;

X, Y, Z – базовые классы.

Здесь все унаследованные компоненты классов X, Y, Z в классе S получают статус доступа private.

Явно изменить умалчиваемый статус доступа при наследовании можно

  • помощью атрибутов доступа – private, protected и public, которые

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


25

атрибут, указанный при

атрибут в базовом классе

атрибут, полученный в

наследовании

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

public

public

public

protected

protected

private

недоступен

protected

public

protected

protected

protected

private

недоступен

private

public

private

protected

private

private

недоступен

Пример

class B

{

protected:

int t;

public:

char u;

private:

int x;

};

struct S : B {}; // наследуемые члены t, uимеют атрибут доступа public

class E : B {}; // t, u имеют атрибут доступа private

class M : protected B {}; // t, u – protected

class D : public B {}; // t – protected, u – public

class P : private B {}; // t, u – private

Таким образом, можно только сузить область доступа, но не расширить.

Тема «Статические поля класса»

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

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

  • этом случае создается статическое поле класса, значение которого определяется количеством объектов в работе. В отличие от обычного поля класса, статическое поле не исчезает при удалении объекта. В некотором


26

смысле статические переменные напоминают глобальные переменные программы. Однако статические переменные, в отличие от глобальных

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

#include

class SimpleClass{

public:

//Статическое поле:

static int m;

//Нестатическое поле:

int n;

void show();

}obj1,obj2;

//Повторное объявление переменной:

int SimpleClass::m;

int main(){

SimpleClass::m=10;

obj1.n=1;

obj2.n=2;

obj1.show();

obj2.show();

obj1.m=100;

obj2.show();

return 0;}

//Описание метода:

void SimpleClass::show()

{

cout << "Static field m = " << m << endl; cout << "Nonstatic field n = " << n << endl; }

Тема «Друзья класса. Использование служебного слова friend» Функции-друзья  это функции, не являющиеся функциями-методами

  • тем не менее имеющие доступ к защищённым и закрытым полям класса. Они должны быть объявлены в теле класса как friend. Например:

class Matrix {

...

friend Matrix Multiply(Matrix m1, Matrix m2);

...

};

Matrix Multiply(Matrix m1, Matrix m2) {

...

}

Здесь функция Multiply может обращаться к любым полям и функциям-методам класса Matrix.


27

Дружественной может быть объявлен как весь класс, так и функция-метод класса. Если класс A — объявлен в классе B как друг, то все собственные (не унаследованные) функции-методы класса A могут обращаться к любым членам класса B. Например:

class Matrix {

...

friend class Vector::getNum( Matrix & ) ;

...

private:

int i;

};

...

class Vector

{

int        GetNum(        Matrix        &        m        ){        return        m.i;}        //обращение        к        закрытому        члену

данных класса Matrix

};

Пример обращения:

void SomeFunction()

{

Matrix m;

Vector v;

int i = Vector::GetNum( m );

}

Четыре важных ограничения, накладываемых на отношения дружественности в C++:

  • Дружественность не транзитивна. Если A объявляет другом B, а B, в свою очередь, объявляет другом C, то C не становится автоматически другом для A. Для этого A должен явно объявить C своим другом.

  • Дружественность не взаимна. Если класс A объявляет другом класс B, то он не становится автоматически другом для B. Для этого должно существовать явное объявление дружественности A в классе B.

  • Дружественность не наследуется. Если A объявляет класс

B своим другом, то потомки B не становятся автоматически друзьями A. Для этого каждый из них должен быть объявлен другом A в явной форме.

  • Дружественность  не  распространяется  на  потомков.

Если класс A объявляет B другом, то B не становится автоматически другом для классов-потомков A. Каждый потомок, если это нужно, должен объявить B своим другом самостоятельно.

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


28

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

Для того, чтобы в подобных случаях не приходилось делать поля публичными, С++ позволяет классу объявлять друзей (friends) - функции и классы, у которых есть полный доступ к начинке класса (такой же доступ, как у его собственных функций). Делается это с помощью ключевого слова friend. В приведенном ниже примере в классе A определяются friend-функция print_fields(A a) и friend-класс B.

//

Forward

declaration

of

class

B;

class

B;

//

class

A

definition

class

A

{

int

n1,

n2;

friend

void

print_fields(A

a);

friend

class

B;

public:

set_fields(n_1,n_2)

{

n1

=

n_1;

n2

=

n_2;

}

};

void

print_fields(A

a)

{

cout

<<

a.n1

<<

"

"

<<

a.n2

<<

endl;

}

Функция print_fields(A a) имеет полный доступ к личным полям класса A, поскольку она объявлена friend-функцией класса. Заметьте - friend-объявление распространяется только на функцию с указанной сигнатурой, в нашем случае print_fields(A), а не на все функции с именем print_fields. Что касается объявления

friend class B;

то оно делает поля класса A доступными для всех функций-членов класса B.

Функции и классы-друзья незаменимы там, где разные типы данных тесно связаны между собой, но реализовывать их в одном классе невыгодно или просто невозможно. Хороший пример этому - С++-стиль ввода-вывода, в котором именно дружественные функции позволяют разрабатывать версии операторов << и >> для вновь создаваемых классов.


29

Тема «Перегрузка операций (операторов) для классов. Функции-операции. Бинарные операции. Унарные операции»

Перегрузка - это возможность поддерживать несколько функций с одним названием, но разными сигнатурами вызова. Рассмотрим пример:

double sqrt ( double x ); //Функция корня для чисел с плавающей точкой int sqrt ( int x ); //Функция корня для целых чисел

...

sqrt(1.5);//В этом случае вызовется функция чисел с плавающей точкой sqrt(7);//А в этом уже для целых чисел

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

Пример операторов:

+, <<, & и т.д.

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

Список операторов:

  • - * / % //Арифметические операторы

+= -= *= /= %=

+a -a //Операторы знака

++a a++ --a a-- //Префиксный и постфиксный инкременты && || ! //Логические операторы

& | ~ ^ &= |= ^=

<< >> <<= >>= //Битовый сдвиг = //Оператор присваивания == != //Операторы сравнения

< > >= <=

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

Строго говоря, изменения происходят только с количеством аргументов в операторных функциях - при перегрузке бинарных операторов


30

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

#include

using namespace std;

//Класс для реализации комплексных чисел:

class MComp{

public:

double Re;

double Im;

//Переопределение оператора сложения методом класса:

MComp operator+(MComp y){

MComp z;

z.Re=Re+y.Re;

z.Im=Im+y.Im;

return z;}

};

int main(){

MComp a,b,c;

a.Re=1;

a.Im=2;

b.Re=2;

b.Im=3;

//Сложение объектов:

c=a+b;

cout << "c.Re = " << c.Re << "\n";

cout << "c.Im = " << c.Im << "\n";

return 0;

}

Тема «Полиморфизм и виртуальные функции»

Семантика системы типов С++ не полиморфна, однако есть несколько способов обеспечить полиморфное поведение. Прежде всего это перегрузка методов классов при наследовании — традиционный для ООП способ обеспечения абстракции данных

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

class Figure

{

void Draw() const;

};

class Square : public Figure

{

void Draw() const;

};


31

class Circle : public Figure

{

void Draw() const;

};

class Window

{

void Draw() const;

};

  • результате компиляции этих определений формируется шесть тел функций. В коде они используются одинаково; выбор конкретного экземпляра функции осуществляется в зависимости от типа экземпляра объекта, для которого осуществляется вызов. Согласованность поведения функций остаётся на совести программиста.

Circle *c = new Circle(0,0,5);

Figure *f = c; // правильно: Figure — базовый класс для Circle c->Draw();

f->Draw(); // Указатели равны друг другу, но для f и c будут вызваны разные функции

SquareWindow *sw = new Window(0,0,5);

sw->Draw(); // используется так же

f = sw; // ошибка! SquareWindow не входит в число наследников Figure!

Как видно, диапазон этого вида полиморфизма в С++ ограничивается на этапе проектирования заданным перечнем типов. По умолчанию такой полиморфизм является статическим, но при использовании спецификатора virtual он превращается в динамический:

class Figure

{...

virtual void Draw() const;

};

class Square : public Figure

{

void Draw() const;

};

class Circle : public Figure

{...

void Draw() const;

};

Figure* figures[10];

figures[0] = new Square(1, 2, 10);

figures[1] = new Circle(3, 5, 8);

...

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

figures[i]->Draw();

  • этом  случае  для  каждого  элемента  массива  будет  вызвана

Square::Draw() или Circle::Draw(), в зависимости от вида фигуры.

Чистой виртуальной функцией называется виртуальная функция-член, которая объявлена со спецификатором = 0 вместо тела:

class Figure


32

{

...

virtual void Draw() const = 0;

);

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

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

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

Тема «Области действия и пространства имён»

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

Например, Андрей работает в компании X, а ID его как работника равен 123. Олег работает в компании Y, а его ID также равен 123. Единственное (с точки зрения некой системы учёта), благодаря чему Андрей

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


33

Наличие двух работников в компании с одинаковыми ID представляет большие проблемы при их использовании, например, по платёжному чеку, в котором будет указан работник с ID 123, будет весьма затруднительно определить работника, которому этот чек предназначается.

Добавлены пространства имён (namespace). Например, если написать

namespace Foo

{

const int x=5;

typedef int** T;

void f(int y) {return y*x};

double g(T);

...

}

то вне фигурных скобок следует обращаться к T, x, f, g как Foo::T, Foo::x, Foo::f и Foo::g соответственно. Если в каком-то файле нужно обратиться к ним непосредственно, можно написать

using namespace Foo;

Или же

using Foo::T;

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

namespace

{

...

}

Все имена, описанные в нём, доступны в текущей единице трансляции

  • больше нигде.

  • Один или несколько последних аргументов функции могут задаваться по умолчанию. К примеру, если функция описана как void f(int x, int y=5, int z=10), вызовы f(1), f(1,5) и f(1,5,10) эквивалентны.

  • При описании функций отсутствие аргументов в скобках означает, в отличие от C, что аргументов нет, а не то, что они неизвестны. Если аргументы неизвестны, надо пользоваться многоточием, например, int printf(const char* fmt, ...).

  • Внутри структуры или класса можно описывать вложенные типы, как через typedef, так и через описание других классов, а

также перечислений. Для доступа к таким типам вне класса, к имени типа добавляется имя структуры или класса и два двоеточия:

struct S

{

typedef int** T;

T x;

};

S::T y;


34

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

Блок

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

Файл

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

Функция

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

Прототип функции

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

Класс

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

Тема «Обработка исключительных ситуаций»

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

  • сложности программ необходимо включить много таких проверок по всей программе.

Основные концепции исключительных ситуаций:

  • Исключительная ситуация (exception) представляет собой неожиданное событие — ошибку — в программе.


35

  • В ваших программах вы определяете исключительные ситуации как классы.

  • Чтобы заставить ваши программы следить за исключительными ситуациями, необходимо использовать оператор C++ try.

  • Для обнаружения определенной исключительной ситуации ваши программы используют оператор C++ catch.

  • Для генерации исключительной ситуации при возникновении ошибки ваши программы используют оператор C++ throw.

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

  • Некоторые (старые) компиляторы не поддерживают исключительные ситуации C++.

Обработка исключений в С++ использует три ключевых слова: try, catch и throw. Те инструкции программы, где ожидается возможность появления исключительных ситуаций, содержатся в блоке try. Если в блоке try возникает исключение, т. е. ошибка, то генерируется исключение. Исклю-чение перехватывается, используя catch, и обрабатывается. Ниже это общее описание будет рассмотрено более подробно.

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

try {

  • блок try

catch (тип1 аргумент) {

// блок catch

catch (тип2 аргумент) {

// блок catch

catch (типЗ аргумент) {

  • блок catch

}

...

catch (типN аргумент) {

  • блок catch

}

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

Когда исключение сгенерировано, оно перехватывается соответствующей инструкцией catch, обрабатывающей это исключение.


36

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

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

throw исключение;

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

Если генерируется исключение, для которого отсутствует подходящая инструкция catch, может произойти аварийное завершение программы. При

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

Ниже представлен пример, иллюстрирующий способ обработки исключений в С++:

  • пример обработки простого исключения

#include

int main()

{

cout << "Start\n";

try { // начало блока try

cout << "Inside try block\n";

throw 100; // генерация ошибки

cout << "This will not execute";

}

catch (int i) { // перехват ошибки

cout << "Caught an exception -- value is: ";

cout << i << " \n";

}

cout << "End";

return 0;

}

Программа выведет на экран следующий текст:

Start

Inside try block


37

Caught an exception -- value is: 100

End

Рассмотрим внимательнее эту программу. Как можно видеть, блок try содержит три инструкции. За ним следует инструкция catch(int i), обрабатывающая исключения целого типа. В блоке try будут выполняться только две инструкции: первая и вторая — throw. Как только исключение было сгенерировано, управление передается инструкции catch, а блок try прекращает свое исполнение. Таким образом, catch не вызывается. Скорее можно сказать, что к ней переходит исполнение программы. Для этого автоматически осуществляется переустановка стека. Таким образом, инст-рукция после инструкции throw никогда не выполняется.

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

Тема «Классы коллекций. Линейные коллекции. Коллекции с прямым доступом. Нелинейные коллекции»

Коллекции представляют собой организацию данных и определяют методы доступа к этим данным. Коллекции подразделяются на две категории: линейные и нелинейные. Линейная коллекция содержит список элементов, упорядоченных по положению. В этом списке имеется первый элемент, второй и т.д. Массив с индексом, отражающим порядок элементов является основным примером линейной коллекции.

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

Коллекции с прямым доступом

Массив  совокупность однотипных элементов, расположенных в смежных ячейках памяти. Для доступа к элементам используют числовой индекс – порядковый номер в наборе.

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


38

Коллекции с последовательным доступом

Более общей коллекцией является список, сохраняющий элементы в определенном порядке. Структура, называемая линейным списком содержит произвольное число элементов. Размер списка изменяется, после удаления и добавления к нему новых элементов. Первый элемент находится в начале или голове списка, а последний – в хвосте списка. Каждый элемент списка имеет связь с последующим, таким образом можно передвигаться от одного элемента к другому. Для доступа к элементам списка необходимо выполнять прохождение от головы до нужной позиции. Если движение по списку можно осуществлять только в одном направлении, то список называется однонаправленным, если в двух направлениях  двунаправленный. Если элемент списка хранит информацию об одном “соседе”, то список называется односвязным, если о двух  двусвязным.

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

Очередь  это список с доступом в начало и конец. Элементы вставляются в конец, а извлекаются из начала списка. Иногда используются очереди приоритетов. В них добавление новых элементов происходит в конец, а удаление – из произвольного места согласно приоритету.

В        машинной        системе        файл                это        коллекция,        имеющая

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

Нелинейные коллекции

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

Группа  представляет те коллекции, которые содержат элементы без упорядочивания. Набор переменных в программе – яркий пример группы.


39

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

Тема «Создание класса для работы со строками»

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

Строковая константа - любая последовательность символов, заключенная в двойные кавычки. Заключенные в кавычки символы и автоматически добавляемый к ним компилятором завершающий символ \0 сохраняется в памяти в качестве символьной строки. Например:

#define STR " Строковая константа" Массивы символьных строк и инициализация.

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

  • инициализация массива с помощью строковой константы. Следующее объявление инициализирует внешний массив m1 символами указанной строки:

char m1[ ] = " Инициализация массива "

Это сокращенная форма стандартной формы инициализации массива :

char m1[ ] = { ‘И’, ’н’, ‘и’, ‘ц’, ‘и’, ‘а’, ‘л’, ‘и’, ‘з’, ‘а’, ‘ц’, ‘и’, ‘я’, ‘ ‘, ‘м’, ‘а’, ‘с’, ‘с’, ‘и’, ‘в’, ‘а’, ‘\0’ };

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

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

char

char


*m3 = " Использование указателя"; m3[ ] = " Использование массива";


40

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

Сравнение представлений в форме массива и указателя.

Так        в        чем        же        состоит        различие        между  представлениями        в        форме

массива  и  указателя?Формамассива

(  m3[ ])  приводит  к  созданию

массива, состоящего из 24 элементов(  по

одному для  каждого  символа

плюс один элемент для завершающего символа ‘\0 ’ ), в статической памяти. Каждый элемент инициализируется соответствующим символом. Отсюда следует, что компилятор будет распознавать имя m3 в качестве синонима адреса элемента массива, &m3[0]. Важно отметить, что в представлении в форме массива m3 - адресная константа. m3 нельзя изменять, поскольку это бы означало изменение ячеек памяти, в которых хранится массив. Для

идентификации   следующего   элемента   массива

можно   использовать

операции типа m3 + 1.

Представление в форме указателя (*m3) также приводит к резервированию в статической памяти места для 24 элементов строки. Кроме

того,  еще  одна  ячейка  резервируется  для  переменной  указателя

m3.

Первоначально  эта  переменная   указывает  на  первый  символ  строки,

но

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

И

н

и

ц

и

а

л

и

з

а

ц

и

я

м

а

с

с

и

в

а

\0

\0

\0

Рис 8 представление строки в ячейках памяти

  • языке С большинство операций со строками в действительности работают с указателями. Рассмотрим несколько строк кода:

char *mesg =        " Don’t be a fool";

char *copy;

copy = mesg;

Сама строка никуда не копировалась. Оператор copy = mesg; всего лишь создает второй указатель, который указывает на ту же строку.

co

D

o

n

t

b

e

a

f

o

o

l

\0

py

mesg

Рис 9 Создание второго указателя.

Почему бы просто не скопировать всю строку? Что эффективнее : скопировать один адрес, или скажем 50 отдельных элементов? Часто для выполнения задачи достаточно получить только адрес.


41

Создадим шаблон класса «строка». Он содержит одно поле - указатель на строку ( *s). В классе выполняются методы: определение длины строки, копирование части строки, печать строки, поиск указанного символа.

Методы:

string (char *st)  конструктор класса, в который мы передаём строку int len_str()  возвращает длину строки

char* sub_str(int i, int j)  копирует часть строки начиная с позиции i и до

позиции j

void print_str()  печатает строку

int kol_sim( char c)  определяет сколько раз встречается указанный символ в строке

Шаблон класса строки

class string {

private:

char *s;

public:

string (char *st);

~string();

int len_str();

char* sub_str(int i, int j);

void print_str();

int kol_sim( char c);

};

Тема «Создание класса ‘дерево’»

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

КОРЕНЬ

левое поддерево


правое поддерево

Ира

левый дочерний узел

Вика


правый дочерний узел

Наташа

Аня        Дина        Лена        Света

NULL        NULL        NULL        NULL        NULL        NULL        NULL NULL

РИС. 10 Хранение слов в дереве двоичного поиска.


42

Идея состоит в том, что каждый узел имеет два дочерних узла: левый и правый. Упорядочение определяется тем, что элемент в левом узле предшествует элементу в родительском узле, а элемент в правом узле следует за элементом в родительском узле. Эта связь сохраняется для каждого узла, имеющего дочерние узлы. Более того, все элементы, которые могут проследить свою родословную к левому узлу родительского узла, содержат элементы, которые предшествуют родительскому элементу. А каждый элемент, ведущий свое происхождение от правого узла, содержит элементы, которые по порядку следуют за родительским элементом. Дерево, показанное на рисунке 3, хранит слова, упорядоченные этим способом. Верхняя часть дерева называется корнем. Дерево - иерархическая организация, а это означает, что данные организованы по рангам, или уровням. Если дерево двоичного поиска полностью заполнено, каждый уровень имеет вдвое больше узлов, чем уровень, расположенный выше него.

Каждый узел в дереве двоичного поиска сам является корнем узлов, исходящих из него, делая узел и его дочерние узлы поддеревом. Например, на рисунке 3 узлы, содержащие слова Вика, Аня, Дина, образуют левое поддерево всего дерева, а слово Света - правое поддерево поддерева Наташа-Лена-Света.

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

  • 2 раза (Двоичный поиск).

Применим этот метод, присутствует ли слово Люда в дереве, показанном на рисунке. Сравнивая элемент Люда с элементом Ира (элемент корневого узла), видим, что элемент Люда, если присутствует, должен находится в правой половине дерева. Поэтому мы обращаемся к правому дочернему узлу и сравниваем Люда с элементом Наташа. В этом случае Люда предшествует узловому элементу, поэтому необходимо следовать за связью к левому узлу. Там мы находит элемент Лена, который предшествует искомому элементу Люда. Теперь необходимо следовать по правой ветви, но она пуста, следовательно, три сравнения показывают, что слово Люда отсутствует в дереве.

Абстрактный тип данных(ADT) - двоичное дерево.

Краткое неформальное описание этого типа:


43

Имя типа:        Дерево двоичного поиска

Свойства типа:        -Двоичное дерево является или пустым набором узлов

(пустым деревом),

или  набором  узлов  с  одним  узлом,  обозначенным  в

качестве корня.

-Каждый узел имеет ровно два исходящих из него дерева, названные левым поддеревом и правым поддеревом.

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

-Дерево двоичного поиска - упорядоченное двоичное дерево, где каждый узел содержит элемент, в котором все элементы в левом поддереве предшествуют корневому элементу и в котором корневой элемент предшествует всем элементам в правом поддереве.

Операции с типом:        Инициализация дерева пустым набором.

Определение того, является ли дерево пустым.

Определение того, является ли дерево полным.

Определение количества элементов в дереве

Добавление элемента к дереву.

Удаление элемента из дерева.

Поиск элемента в дереве.

Посещение каждого элемента в дереве.

Тема «Создание класса ‘стек’»

Стек (stack)- это список элементов, доступных только в одном конце списка. Элементы удаляются или добавляются из списка только в вершине стека (top).

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

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

Рис. 11        Пример стека.


44

  • структуре стека важнейшее место занимают операции добавления и удаления элементов. Операция Push добавляет элемент в вершину стека. Об операции удаления Pop из стека говорят как об извлечении из стека. На рисунке приведена последовательность операций Push и Pop .

С

В

В

В

D

А

А

А

А

А

A

Push A

Push B

Push C

Pop

Pop

Push D

Рис 12. Помещение в стек и извлечение из него.

Последний вставленный в стек элемент является первым удаляемым элементом. По этой причине о стеке говорят, что он имеет порядок LIFO ( последним пришел \ первым ушёл). Абстрактное понятие стека допускает неопределённо большой список. Логически подносы в столовой могут складываться бесконечно. В действительности подносы находятся на полке. Когда полка переполнена, мы не можем добавить ещё один элемент в стек. Стек достигает максимального количества элементов, которыми он может управлять. Эта ситуация поясняет значение условия полного стека (stack full). Другая крайность  вы не можете взять поднос с пустой полки. Условие пустого стека (stack empty) подразумевает, что вы не можете удалить элемент (Pop). Верхняя граница максимального количества элементов задаётся в классе полем Stacksize.

.

Класс Stack.

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

Объявление объекта Stack включает размер стека, который определяет максимальное количество элементов. Размер имеет значение по умолчанию Stacksize=50. Массив, индекс элемента top являются закрытыми полями, а операции над стеком – открытыми.

Первоначально стек пуст и top=-1. элементы вводятся в массив ( функция Push) в возрастающем порядке индексов (top = 0,1,2 ) и извлекаются из стека ( функция Pop) в убывающем порядке индексов (2, 1, 0).

Шаблон класса «стек»

const int Stacksize=50;

typedef DataType;

class Stack{

private:

DataType stacklist[Stacksize];

int top; // индекс верхнего элемента в стеке


45

public:

//конструктор инициализирует вершину

Stack();

  • операции модификации стека void Push(DataType item); DataType Pop ();

void ClearStack();

  • доступ к элементу в вершине стека

DataType Peek();

  • методы проверки стека

bool StackEmpty(); // метод проверяет пуст ли массив bool StackFull(); // метод проверяет полон ли массив

};

Тема «Создание класса ‘очередь’»

Очередь (queue)  это структура данных, которая сохраняет элементы в массиве и обеспечивает доступ к данным только в двух концах массива. Элемент вставляется в конец списка, а удаляется из начала списка. Приложение использует очередь для хранения элементов в порядке их поступления.

Удалить

34

67

23

56

элемент

Начало

Конец

Добавить

элемент

Рис 13. Структура «очередь»

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

Операции стека:

Добавление элемента «А»:

Началоконец

Добавление элемента «В»:

  • В    

Началоконец

Добавление элемента «С»:

А

В

С

Начало

конец

Удаление элемента «А»:

А

А

В

С


46

  • С    

Началоконец

Удаление элемента «В»:

Началоконец

Класс «Queue»

Класс Queue использует массив для сохранения элементов и определяет переменные, которые поддерживают позиции front (первая позиция) и last (последняя позиция). Класс обязательно должен содержать метод, который определяет заполнена очередь или нет.

Шаблон класса «Queue»

const int size=50;

typedef DataType;

class Queue {

private:

DataType qlist[size];

int last; // индекс последнего элемента в очереди int front; // индекс первого элемента в очереди int count; // количество элементов в очереди

public:

//конструктор инициализирует вершину

Queue ();

  • операции модификации очереди void Insert(DataType item); DataType Delete ();

void ClearQueue ();

// доступ к первому элементу в очереди

DataType QFront();

  • методы проверки очереди

bool QEmpty(); // метод проверяет пуста ли очередь

bool QFull();        // метод проверяет переполнена ли очередь

};


47

Литература.

  1. Архангельский   А.Я.Программирование  в  среде   C++  Builder.  Издательство

“Бином”, 2000

  1. Гради Буч. Объектно-ориентированный анализ и проектирование.-М.: ЗАО “Издательство БИНОМ”, 1999.

  1. Джеймс Рамбо, Айвар Джекобсон. UML- руководство пользователя. –СПб.: Издательство “ДМК”, 1999

  1. Петер Коунд . Объектные модели. Стратегии , шаблоны, приложения. :Издательство “Лори”, 1999.

  1. Галяров И.Р. Borland C++ 5 для себя. -Издательство “ДМК”, 2001



По теме: методические разработки, презентации и конспекты

Рабочая программа междисциплинарного курса 06.01«Изобразительная деятельность и декоративно-прикладное искусство» Специальность: 44.02.01 – Дошкольное образование

Рабочая программа междисциплинарного курса 06.01 «Изобразительная деятельность и декоративно-прикладное искусство» является частью программ подготовки специалистов среднего звена в соответствии с ФГОС...

Курс лекций, практических работ и вопросов по дисциплине "Основы теории информации" для специальности 230701 Прикладная информатика (по отраслям)

1.1. Область применения  рабочей программыРабочая программа учебной дисциплины «Основы теории информации»  является частью  основной профессиональной образовательной программы...

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

Уважаемые обучающиеся, данный краткий Курс лекций по литературе 1 половины  19 века поможет вам в подготовке к занятиям и экзамену по литературе....

курс лекций для дистанционного обучения по специальности "медицинский регистратор" Тема "Первая помощь" (лекция №1)

представлен материал для дистанционного обуывения оп теме "Первая помощь" при подготовке медицинских регистратраторов...

Курс лекций по Метрологии, стандартизации и сертификации по специальности 18.02.09_ 1 часть

Курс лекций по Метрологии, стандартизации и сертификации по специальности Переработка нефти и газа...