- •Предисловие
- •Введение
- •Введение в программирование
- •1.1. Предисловие к курсу
- •1.2. Идеология языка
- •1.3. Обзор среды Microsoft Developer Studio
- •1.4. Жизненный цикл программного обеспечения
- •1.5. Общая структура программы
- •1.6. Директивы препроцессора
- •1.7. Построение исполняемого файла
- •1.8. Строительные блоки программы
- •Контрольные вопросы
- •Типы данных. Переменные. Массивы. Операции и Указатели
- •Стандартные типы и размеры данных
- •2.1.1. Объявление переменных
- •Управляющие символьные константы
- •2.2. Объявление указателя
- •2.2.1. Операции разыменования и взятия адреса
- •2.2.2. Указатели на указатели
- •2.2.3. Арифметические операции с указателями
- •2.3. Массивы
- •2.3.1. Инициализация массивов
- •2.3.2 Динамические массивы
- •2.3.3. Методы доступа к элементам массивов
- •2.3.4. Массивы указателей
- •2.4. Строки
- •2.5. Операции
- •2.5.1. Арифметические операции
- •Арифметические операции
- •2.5.2 Операции сравнения и логические операции
- •Операции сравнения и логические операции
- •2.5.3. Побитовые операции
- •Побитовые операции
- •Контрольные вопросы
- •3.1. Базовые операторы
- •3.1.1. Оператор выражение
- •3.2.2. Оператор switch
- •3.3.4. Оператор goto
- •3.4. Операторы цикла
- •3.4.1. Оператор for
- •3.4.2. Оператор while
- •3.4.3. Оператор do..While
- •Контрольные вопросы
- •Стандартный ввод/вывод. Работа с файлами.
- •4.1. Роль стандартного ввода/вывода
- •4.1.1. Основные функции стандартного ввода/вывода
- •4.2. Понятие файла
- •4.2.1. Строение файлов
- •4.2.2. Порядок работы с файлом
- •4.2.3. Обзор библиотечных функций с для работы с файлами
- •4.3. Программные конструкции при работе с файлами
- •4.3.1. Открытие/закрытие файла
- •4.3.2. Цикл посимвольного чтения содержимого файла
- •4.3.3. Цикл построчного чтения содержимого файла
- •Контрольные вопросы
- •Функция. Пользовательские типы данных.
- •5.1. Понятие функции
- •5.1.1. Определение функции
- •5.1.2. Формальные параметры
- •5.1.3. Тип возвращаемого значения
- •5.1.4. Тело функции
- •5.1.5. Фактические параметры
- •5.1.6. Рекурсивные вызовы
- •5.1.7. Передача параметров
- •5.1.8. Библиотеки стандартных функций
- •5.2. Пользовательские типы данных.
- •5.2.1. Ключевое слово typedef
- •5.2.2. Перечислимый тип данных
- •5.2.3. Понятие структуры
- •5.2.4. Указатели на структурный объект
- •Контрольные вопросы
- •Работа с динамической памятью. Динамические структуры данных
- •6.1. Работа с динамической памятью
- •6.1.1. Статическое и динамическое распределение памяти
- •6.1.2. Основные принципы динамического распределения
- •6.1.3. Способы работы с динамической памятью
- •6.2. Динамические структуры данных
- •6.2.1. Стек
- •6.2.2.Линейный список
- •Контрольные вопросы
- •Объектно-ориентированное программирование
- •7.1. Критерии качества декомпозиции проекта
- •7.2. Новые концепции программирования
- •7.3. Достоинства ооп
- •7.4. Объекты и классы в ооп
- •7.4.1. Определение класса
- •7.4.2. Использование класса
- •7.4.3. Вложенные классы
- •Контрольные вопросы
- •Конструкторы и Перегрузка операций.
- •8.1. Перегрузка операций
- •8.1.1. Перегрузка операций внешними функциями
- •8.1.2. Перегрузка операций методами класса
- •8.2. Конструкторы и деструктор
- •8.2.1. Конструкторы и параметры
- •Контрольные вопросы
- •9.1. Простое открытое наследование
- •9.1.1 Конструкторы и деструкторы при наследовании
- •9.1.2. Поля и методы при наследовании
- •9.1.3. Вложенные классы и наследование
- •9.1.4. Закрытое наследование
- •9.1.5. Виртуальные функции
- •9.1.6. Чистые виртуальные функции и абстрактные классы
- •9.3. Основы программирования под Windows
- •9.3.1. Типы данных в Windows
- •9.4. Cреда Microsoft Developer Studio
- •9.4.1. Библиотека mfc
- •9.4.2. Архитектура приложения
- •9.4.3. Каркас приложения
- •9.4.4. Проект приложения
- •Контрольные вопросы
- •Заключение
- •Список Литературы
6.2.2.Линейный список
Специфика этой задачи состоит в том, что размер базы не ограничен, поэтому для ее хранения в оперативной памяти воспользуемся односвязным линейным списком. Каждый элемент односвязного списка включает информационную часть (фамилию, год рождения, оклад) и указатель на следующий элемент. Признаком конца списка служит ноль в поле указателя. Список доступен через указатель на его первый элемент:
const int l_name = 31;
struct Man // Элемент списка
{
char name[l_name];
int birth_day;
float pay;
Man *next;
};
Man *beg; // Указатель на начало списка
Основное внимание при программировании этой задачи уделяется разбиению на функции и спецификации их интерфейсов. Например, логично оформить в виде функции каждую операцию со списком (формирование, поиск, добавление и удаление элемента), поскольку они представляют собой законченные действия.
Контрольные вопросы
1. Статическое и динамическое распределение памяти.
2. Основные принципы динамического распределения памяти.
3. Способы работы с динамической памятью.
4. Динамические структуры данных.
5. Стек.
6. Линейный список.
Лекция 7.
Объектно-ориентированное программирование
Объектно-ориентированное программирование (ООП) – это технология, возникшая как реакция на очередную фазу кризиса программного обеспечения, когда методы структурного программирования уже не позволяли справляться с растущей сложностью промышленного программного продукта.
Существенная черта промышленной программы – ее сложность: один разработчик не в состоянии охватить все аспекты системы, поэтому в ее создании участвует целый коллектив.
Так как сложные системы разрабатываются в расчете на длительную эксплуатацию, то появляются еще две проблемы: сопровождение системы (устранение обнаруженных ошибок) и ее модификация, поскольку у заказчика постоянно появляются новые требования и пожелания.
Способ управления сложными системами был известен еще в древности – divide et impera (разделяй и властвуй). То есть выход – в декомпозиции системы на все меньшие и меньшие подсистемы, каждую из которых можно совершенствовать независимо. Но если в рамках структурного подхода декомпозиция понимается как разбиение алгоритма, тогда каждый из модулей системы выполняет один из этапов общего процесса, то ООП предлагает совершенно другой подход.
Суть его в том, что в качестве критерия декомпозиции принимается принадлежность ее элементов к различным абстракциям проблемной области. Для каждого из этих объектов определяются свойства, существенные для решения задачи. Затем каждому реальному объекту предметной области ставится в соответствие программный объект.
Почему объектно-ориентированная декомпозиция оказалась более эффективным средством борьбы со сложностью процессов проектирования и сопровождения программных систем, чем функциональная декомпозиция? Чтобы в них разобраться, рассмотрим критерии качества проекта, связанные с его декомпозицией.
7.1. Критерии качества декомпозиции проекта
Со сложностью приложения трудно что-либо сделать – она определяется целью создания программы. А вот сложность реализации можно попытаться контролировать.
Первый вопрос, возникающий при декомпозиции: на какие компоненты (модули, функции, классы) нужно разбить программу? Очевидно, что с ростом числа компонентов сложность программы растет, поскольку необходима кооперация, координация и коммуникация между компонентами. Особенно негативны последствия неоправданного разбиения на компоненты, когда оказываются разделенными действия, по сути тесно связанные между собой.
Вторая проблема связана с организацией взаимодействия между компонентами. Взаимодействие упрощается и его легче взять под контроль, если каждый компонент рассматривается как некий «черный ящик», внутреннее устройство которого неизвестно, но известны выполняемые им функции, а также «входы» и «выходы» этого ящика. Вход компонента позволяет ввести в него значение некоторой входной переменной, а выход – получить значение некоторой выходной переменной. В программировании совокупность входов и выходов черного ящика определяет интерфейс компонента. Интерфейс реализуется как набор некоторых функций (или запросов к компоненту), вызывая которые клиент либо получает какую-то информацию, либо меняет состояние компонента.
Модное нынче словечко «клиент» означает просто-напросто компонент, которому понадобились услуги другого компонента, исполняющего в этом случае роль сервера. Взаимоотношение клиент/сервер на самом деле очень старо и использовалось уже в рамках структурного программирования, когда функция-клиент пользовалась услугами функции-сервера путем ее вызова.
Синонимами являются термины структурная, процедурная и алгоритмически-ориентированная декомпозиция.
Подытожим сказанное о проблемах разбиения программы на компоненты и организации их взаимодействия. Для оценки качества программного проекта нужно учитывать, кроме всех прочих, следующие два показателя:
- сцепление (cohesion) внутри компонента – показатель, характеризующий степень взаимосвязи отдельных его частей.
Простой пример: если внутри компонента решаются две подзадачи, которые легко можно разделить, то компонент обладает слабым (плохим) сцеплением.
- связанность (coupling) между компонентами – показатель, описывающий интерфейс между компонентом-клиентом и компонентом-сервером. Общее число входов и выходов сервера есть мера связанности. Чем меньше связанность между двумя компонентами, тем проще понять и отслеживать в будущем их взаимодействие. А так как в больших проектах эти компоненты часто разрабатываются разными людьми, то очень важно уменьшать связанность между компонентами.
Заметим, что описанные показатели, конечно, имеют относительный характер, и пользоваться ими следует благоразумно. Например, фанатичное следование первому показателю (сильное сцепление) может привести к дроблению проекта на очень большое количество мелких функций, и сопровождающий программист вряд ли помянет вас добрым словом.
Почему в случае функциональной декомпозиции трудно достичь слабой связанности между компонентами? Дело в том, что интерфейс между компонентами в таком проекте реализуется либо через глобальные переменные, либо через механизм формальных/фактических параметров. В сложной программной системе, реализованной в рамках структурной парадигмы, практически невозможно обойтись без связи через глобальные структуры данных, а это означает, что фактически любая функция в случае ошибки может испортить эти данные. Подобные ошибки очень трудно локализуются в процессе отладки. Добавьте к этому головную боль для сопровождающего программиста, который должен помнить десятки (если не сотни) обращений к общим данным из разных частей проекта. Соответственно, модификация существующего проекта в связи с новыми требованиями заказчика также потребует очень большой работы, так как возникнет необходимость проверить влияние внесенных изменений практически на все компоненты проекта.
Другой проблемой в проектах с функциональной декомпозицией было «проклятие» общего глобального пространства имен. Члены команды, работающей над проектом, должны были тратить немалые усилия по согласованию применяемых имен для своих функций, чтобы они были уникальными в рамках всего проекта.