СДЕЛАЙТЕ СВОИ УРОКИ ЕЩЁ ЭФФЕКТИВНЕЕ, А ЖИЗНЬ СВОБОДНЕЕ

Благодаря готовым учебным материалам для работы в классе и дистанционно

Скидки до 50 % на комплекты
только до

Готовые ключевые этапы урока всегда будут у вас под рукой

Организационный момент

Проверка знаний

Объяснение материала

Закрепление изученного

Итоги урока

Лекционный материал "Знакомство с объектно-ориентированным программированием. Классы"

Категория: Информатика

Нажмите, чтобы узнать подробности

Лекционный материал предназначен для использования в образовательном процессе преподавателями профессионального цикла по освоению темы «Знакомство с ООП. Классы» на дисциплине "Основы алгоритмизации и программирования" для специальности» 09.02.07 Информационные системы и программирование.

Просмотр содержимого документа
«Лекционный материал "Знакомство с объектно-ориентированным программированием. Классы"»

Задания.

  1. Нарисовать интеллект-карту, отражающую все темы и основные определения, что мы прошли до сегодняшнего занятия. Центральное понятие – программа, исходящие ветки – среда программирования, переменные и константы, базовые алгоритмы.

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


Знакомство с объектно-ориентированным программированием


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

Обычное программирование (процедурное)

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

Например, в интернет-магазине может быть функция «Проверить email». Она получает на вход какой-то текст, сопоставляет со своими правилами и выдаёт ответ: это правильный электронный адрес или нет. Если правильный, то true, если нет — то false.

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

Что не так с процедурным программированием

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

Например, вы пишете функцию «Зарегистрировать пользователя интернет-магазина». Внутри неё вам нужно проверить его электронный адрес. Вы вызываете функцию «Проверить email» внутри функции «Зарегистрировать пользователя», и в зависимости от ответа функции вы либо регистрируете пользователя, либо выводите ошибку. И у вас эта функция встречается ещё в десяти местах. Функции как бы переплетены.

Тут приходит продакт-менеджер и говорит: «Хочу, чтобы пользователь точно знал, в чём ошибка при вводе электронного адреса». Теперь вам нужно научить функцию выдавать не просто true — false, а ещё и код ошибки: например, если в адресе опечатка, то код 01, если адрес спамерский — код 02 и так далее. Это несложно реализовать.

Вы залезаете внутрь этой функции и меняете её поведение: теперь она вместо true — false выдаёт код ошибки, а если ошибки нет — пишет «ОК».

И тут ваш код ломается: все десять мест, которые ожидали от проверяльщика true или false, теперь получают «ОК» и из-за этого ломаются.


Теперь вам нужно:

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

  • либо переделать сам проверяльщик адресов, чтобы он остался совместимым со старыми местами, но в нужном вам месте как-то ещё выдавал коды ошибок;

  • либо написать новый проверяльщик, который выдаёт коды ошибок, а в старых местах использовать старый проверяльщик.

Задача, конечно, решаемая за час-другой.

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

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

Объектно-ориентированное программирование

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

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

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

Объект можно представить как независимый электроприбор у вас на кухне. Чайник кипятит воду, плита греет, блендер взбивает, мясорубка делает фарш. Внутри каждого устройства куча всего: моторы, контроллеры, кнопки, пружины, предохранители — но вы о них не думаете. Вы нажимаете кнопки на панели каждого прибора, и он делает то, что от него ожидается. И благодаря совместной работе этих приборов у вас получается ужин.

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

Инкапсуляция, абстракция, наследование, полиморфизм

Инкапсуляция — объект независим: каждый объект устроен так, что нужные для него данные живут внутри этого объекта, а не где-то снаружи в программе. Например, если у меня есть объект «Пользователь», то у меня в нём будут все данные о пользователе: и имя, и адрес, и всё остальное. И в нём же будут методы «Проверить адрес» или «Подписать на рассылку».

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

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

Например, над магазином работают два программиста: один пишет модуль заказа, а второй — модуль доставки. У первого в объекте «заказ» есть метод «отменить». И вот второму нужно из-за доставки отменить заказ. И он спокойно пишет: «заказ.отменить()». Ему неважно, как другой программист будет реализовывать отмену: какие он отправит письма, что запишет в базу данных, какие выведет предупреждения.

Наследование — способность к копированию. ООП позволяет создавать много объектов по образу и подобию другого объекта. Это позволяет не копипастить код по двести раз, а один раз нормально написать и потом много раз использовать.

Например, у вас может быть некий идеальный объект «Пользователь»: в нём вы прописываете всё, что может происходить с пользователем. У вас могут быть свойства: имя, возраст, адрес, номер карты. И могут быть методы «Дать скидку», «Проверить заказ», «Найти заказы», «Позвонить».

На основе этого идеального пользователя вы можете создать реального «Покупателя Ивана». У него при создании будут все свойства и методы, которые вы задали у идеального покупателя, плюс могут быть какие-то свои, если захотите.

Идеальные объекты программисты называют классами.

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

При этом внутри объекта методы могут быть реализованы по-разному. Например, удалить товар — это выдать предупреждение, а потом пометить товар в базе данных как удалённый. А удалить пользователя — это отменить его покупки, отписать от рассылки и заархивировать историю его покупок. События разные, но для программиста это неважно. У него просто есть метод «Удалить()», и он ему доверяет.

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

Плюсы и минусы ООП

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

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

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

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

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

А теперь про минусы:

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

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

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

Одно из преимуществ ООП — не нужно много раз писать один и тот же код. Можно однажды придумать какую-то красивую штуку и потом заново её использовать буквально одной строкой. Для этого и нужны классы.

Что есть класс

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

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

Например: 

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

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

Пример: объекты, классы и функции в игре

Допустим, вы пишете компьютерную игру. У вас есть герой, которым управляет игрок, и много врагов, которыми управляет компьютер. И герой, и враги могут стрелять и лечиться. 

Если бы вы писали всё на функциях, у вас были бы такие функции: 

Герой_выстрелить();

Герой_подлечиться();

Враг1_выстрелить();

Враг99_выстрелить();

Враг1_подлечиться();

Враг99_подлечиться();

Это довольно большой зоопарк из функций, делать так не надо. Гораздо лучше создать объекты, а внутрь к ним положить нужные функции: 

Герой.выстрелить();

Герой.подлечиться();

Враг1.выстрелить();

Враг99.подлечиться();

Представим, что каждый персонаж игры — это объект. Внутри объекта что-то лежит 

Чтобы не прописывать все эти функции и объекты вручную, мы создадим класс «Персонаж»:

Класс «Персонаж»{

имя: тут будет имя;

тип: герой или враг;

здоровье: 100;

функция «Выстрелить» {тут описываем, как стрелять};

функция «Подлечиться» {тут описываем, как лечиться};

}

Теперь мы можем создавать сколько угодно персонажей, например, так: 

Новый Персонаж (имя:Герой);

Новый Персонаж (имя:Враг1, здоровье: 10);

Новый Персонаж (имя:Враг2, здоровье: 20);

Допустим, мы решили добавить в нашу игру систему инвентаря. Чтобы не ходить по всем нашим врагам и героям и не копипастить в них код, мы пропишем эту систему внутри класса: 

Класс «Персонаж»{

имя: тут будет имя;

тип: герой или враг;

здоровье: 100;

функция «Выстрелить» {тут описываем, как стрелять};

функция «Подлечиться» {тут описываем, как лечиться};

инвентарь: [сапоги-скороходы, меч-кладенец];

функция «Сбросить_инвентарь» {как сбрасывать};

функция «Подобрать_предмет» {как подбирать};

}

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

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

Где применять классы и ООП, а где — функции

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

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


Классы


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

В общем виде определение класса записывается так:

class имя

{

public:

protected:

private:

};

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

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

имя_класса имя_объекта;

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

  1. С помощью квалифицированных имен

имя объекта.имя класса::имя/поле/метод

. – операция выбора

:: - операция разрешения области видимости

  1. С помощью уточненных имен.

имя объекта.имя поля/метода


Когда метод класса определяется вне класса, то перед именем метода ставится приставка из имени класса и операции разрешения области видимости.

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

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


Свойства методов класса

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

Метки доступа

Метка

Значение

public:

(общедоступные)

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

private:

(закрытый)

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

protected:

(защищенный)

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


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

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

#include

using namespace std;

class beta

{

public:

int d; //число

inr m; //месяц

//прототип метода для вывода даты

void inputd();

};

//определение метода для печати даты

void beta::inputd()

{

cout

cout

}

int main()

{

setlocale(LC_ALL, "RUS");

//объявляем 2 объекта класса beta

beta x, y;

//ввод объекта x

coutn Ввести объект x\n";

coutn Ввести число: \n";

cinx.beta::d;

coutn Ввести месяц: \n";

cinx.beta::m;

//ввод объекта y

coutn Ввести объект y\n";

coutn Ввести число: \n";

ciny.beta::d;

coutn Ввести месяц: \n";

ciny.beta::m;

//вывести объект x

coutn Объект x\n";

x.beta::inputd();

//вывести объект y

coutn Объект y\n";

y.beta::inputd();

//сравнение объектов

if (x.d==y.d && x.m==y.m)

coutn Даты одинаковые";

else coutn Даты разные"

coutendl;

}


Открытые и закрытые элементы

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

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

Метод доступа возвращает либо само закрытое поле, либо что-то эквивалентное ему.

Пример. Переопределим класс beta, сделав поля класса закрытыми.

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

#include

using namespace std;

class beta

{

private:

int d; //число

int m; //месяц

public:

//прототип метода доступа к полю m

int getm();

//прототип метода доступа к полю d

int getd();

//прототип метода инициализации полей

void set(int nm, int nd);

//прототип метода для печати объекта

void outputd();

};

//определение метода доступа к полю m

int beta::getm()

{

return m;

}

//определение метода доступа к полю d

int beta::getd()

{

return d;

}

//определение метода для инициализации полей

void beta::set(int nm, int nd)

{

m=nm;

d=nd;

}

//определение метода вывода объекта

void beta::outputd()

{

coutnЧисло "dm;

}

int main()

{

setlocale(LC_ALL, "RUS");

//объявляем 2 объекта класса beta

beta x, y;

//инициализируем объект x

x.set(7, 8);

coutОбъект x\n";

x.outputd();

//инициализируем объект y

y.set(10, 10);

coutОбъект y\n";

y.outputd();

//сравнение

if (x.getm()==y.getm() && x.getd()==y.getd())

cout

else cout

cout

return 0;

}

Домашнее задание:

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

Описать этот же класс на C++.


Скачать

Рекомендуем курсы ПК и ППК для учителей

Вебинар для учителей

Свидетельство об участии БЕСПЛАТНО!