Автор Анна Евкова
Преподаватель который помогает студентам и школьникам в учёбе.

Основные понятия объектно-ориентированного программирования (Базовые понятия)

Содержание:

Введение

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

Созданный в 1983 году язык программирования C++ до сих пор активно развивается и способен выполнять все вышеуказанные задачи. Он позволяет писать структурированный высокопроизводительный код и используется для широкого списка задач от микроконтроллеров, до научных вычислений и компьютерных игр.

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

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

Для этого требуется выполнить следующие задачи:

  1. Изучить понятия типов данных и самые распространенные из них в языке C++;
  2. Изучить алгоритмические структуры: условия и циклы;
  3. Изучить основных понятий ООП в рамках C++ (классы, объекты, полиморфизм и др.);
  4. Изучить особенности организации кода в C++;
  5. Самостоятельно разработать учебное приложение на языке C++ с использование объектно-ориентированного подхода.

Глава 1. Язык C++

История языка

Язык C++ был создан одним из работников известной компании Bell Labs - Бьерном Страуструпом. Ему хотелось объединить силу и скорость языка C с высокоуровневой абстракцией, которую давали такие языки, как Simula или Smaltalk. Так он начал работу над новым языком, которые сперва назывался C с классами. Первоначально язык разрабатывался самим Страуструпом и лишь в конце 90-ых годов был опубликован первый международный стандарт известный как C++ 98 разработанный специальным комитетом из ISO (International Organization for Standardization – международной организация стандартизации).

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

Несмотря на свой возраст, язык до сих пор активно поддерживается и развивается. В последние годы новые стандарты языка выпускаются каждые 3 года – C++ 11, C++ 14, C++ 17 и уже известны некоторые нововведения C++ 20.

Базовые понятия

Любая программа на языке C++ начинается с определения функции main (Рисунок 1).

Рисунок 1. Функция main.

Запуск программы по сути является запуском данной функции. Ключевое слово int означает тип данных, которые возвращает функция. Операция возвращения данных происходит с помощью ключевого слова return. В случае с функцией main любое число отличное от нуля означает, как правило, что во время выполнения программы произошла какая-то ошибка.

В состав языка входят различные библиотеки, которые можно использовать сразу же. Одна из самых часто используемых на этапе изучения языка библиотек – iostream (in-out stream – поток ввода-вывода). Подключение библиотеки выполняется с помощью инструкции #include <название библиотеки>;. Библиотека iostream позволяет использовать такие методы, как std::cout и std::cin для вывода текста в консоль и считывания ввода из консоли соответственно (Рисунок 2).

Рисунок 2. Использование библиотеки iostream.

Здесь мы на 1 строчке подключаем библиотеку. На 5 строке в консоль выводится традиционное сообщение «Hello, world!». Символ \n означает перевод строки.

На 6 строке объявляется переменная с типом int и на 7 строке ей присваивается значение, введенное пользователем в консоли. Переменную можно представить, как некий ящик, в котором хранится то или иное значение. Тип данных означает, что в этот ящик можно положить только определенные значения. Запись вида int a; называется объявлением переменной, а запись a = 2; - инициализацией переменной. При желании можно объявлять переменные и производить их инициализацию в одном выражении (например, int a = 22;).

Типы данных

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

Целочисленные переменные обозначаются short (short int), int, long (long int) и long long (long long int). Они занимают разное количество байт и соответственно могут числа в определенном диапазоне. Кроме того, каждому типу может предшествовать ключевое слово signed или unsigned. Слово unsigned означает, что переменная не может хранить отрицательные числа, что увеличивает максимальное положительное число, которое можно в неё записать.

Таблица 1.

Целочисленные типы данных

Тип данных

Знак

Диапазон

Размер, бит

short

signed

от -32767 до 32767

>=16

short

unsigned

от 0 до 65535

>=16

int

signed

от -2 147 483 647 до 2 147 483 647

>=32

int

unsigned

от 0

до 4 294 967 295

>=32

long

signed

± 9,22 · 1018

>=64

long

unsigned

от 0 до 1,84 · 1019

>=64

long long

signed

± 9,22 · 1018

>=64

long long

unsigned

от 0 до 1,84 · 1019

>=64

Нужно отметить, что размер и соответственно диапазон варьируется в зависимости от процессора и компилятора. При этом всегда гарантировано, что sizeof(long long) >= sizeof(long) >= sizeof(int) >= sizeof(short), где sizeof() возвращает размер. Таким образом, в переменную типа long гарантировано можно записать значение из переменных типа int и short.

Для записи чисел с плавающей точкой используются переменные типа float, long float и double. При этом long float и double имеют, как правило, один и тот же размер.

Таблица 2.

Типы данных с плавающей точкой

Тип данных

Знак

Диапазон

Размер, бит

float

signed

от -2 147 483 648.0

до 2 147 483 647.0

32

float

unsigned

от 0

до 4 294 967 295.0

32

long float

signed

± 9,22 · 1018

64

long float

unsigned

от 0 до 1,84 · 1019

64

double

signed

± 9,22 · 1018

64

double

unsigned

от 0 до 1,84 · 1019

64

Для представления символов используется тип char, который занимает 1 байт. Такой же по размеру тип bool используется для представления логических значений true/false.

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

Рисунок 3. Использование библиотеки string.

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

Рисунок 4. Использование типа vector.

Операторы

Как и во многих других языках программирования в C++ имеется стандартный набор операторов. Кратко рассмотрим их.

Арифметические операторы известны всем со школы. Это сложение (+), вычитание (-), умножение (*) и деление (/). Кроме них присутствует также оператор получения остатка от деления (%). Как и в математике, в C++ можно использовать скобки для обозначения приоритетности действий. Это касается всех операторов, а не только арифметических. Помимо этого, имеются операторы инкремента (++) и декремента (--). Они увеличивают и уменьшают значение переменной на 1 соответственно. При этом выражение int b = a++; сперва присвоит переменной b значение переменной a, и уже потом увеличит её значение. Выражение же int b = ++a; сперва увеличит значение переменной, а потом уже присвоит его переменной b.

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

Таблица 3.

Логические операторы

Оператор

Название

>

Больше

>=

Больше или равно

<

Меньше

<=

Меньше или равно

==

Равно

!=

Не равно

У отдельных классов могут быть определены свои операторы. Например, у уже рассмотренного объекта std::cout присутствует оператор <<. Кроме этого, отдельные операторы могут быть переопределены для отдельных классов. Так у класса std::string переопределен оператор + таким образом, что две строки соединяются в одну. Более подробно переопределение операторов рассмотрено во второй главе.

Условия и циклы

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

Рисунок 5. Использование условий.

Любое условие начинается с ключевого слова if, после которого в круглых скобках пишется логическое выражение. После этого в фигурных скобках пишется код, который будет выполнен. При необходимости можно добавить блок else, который обозначается соответствующим ключевым словом. Последующий код будет выполнен только в том случае, когда логическое выражение равно false. Между блоками if и else можно вставлять любое количество блоков else if. Таким образом, если первое логическое условие не равно true проверяется условие в блоке else if. Если оно не выполнено, то проверяется условие в следующем таком блоке или же выполняется код в блоке else.

Бывают случаи, когда блоков else if становится достаточно много, что затрудняет чтение кода. В таких случаях используют выражение switch (Рисунок 6).

Рисунок 6. Использование конструкции switch.

Здесь значение, идущее в скобках после ключевого слова switch, сравнивается со значениями после ключевого слова case и в случае их совпадения, выполняется код после двоеточия. Ключевое слово default используется для выполнения кода в случаях, когда проверяемое значение не совпало ни с одним из перечисленных в case-блоках значений. Стоит обратить внимание на то, что каждый блок заканчивается ключевым словом break, которое прерывает дальнейшую проверку. Если его не указывать, то при наличии блока default, код в нем будет выполняться всегда независимо от того, было ли совпадение ранее или нет. Так происходит, потому что после совпадения с одним из значений, программа продолжает проверять последующие значения, в том числе выполняя код в блоке default.

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

Цикл for используется обычно, когда известно точно, сколько раз нужно прогнать код (Рисунок 7).

Рисунок 7. Цикл for.

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

Каждая из перечисленных частей может быть опущена. Так инициализация переменной-счетчика может происходить до конструкции for. Если опущено условие, то цикл будет бесконечным и программа зависнет, если не предусмотреть выход из тела цикла. Бесконечный циклы могут быть очень полезными, но работать с ними нужно с большой осторожностью. Такой цикл также может возникнуть если опущена третья часть – тогда условие всегда будет верным. Опущены могут быть также все три части (Рисунок 8).

Рисунок 8. Цикл for без использования выражений в круглых скобках.

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

Цикл for можно также использовать для перебора значений вектора (Рисунок 9).

Рисунок 9. Перебор элементов вектора.

Нередко бывает, что определенный код нужно выполнить только пока действительно какое-то условие. При этом неизвестно заранее, сколько повторов цикла это потребует. В этом случае используют цикл while (Рисунок 10).

Рисунок 10. Цикл while.

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

Аналогично работает цикл do…while с тем лишь отличием, что код в блоке после ключевого слова do выполнится даже если условие неверно. Такое условие используется, когда код должен быть выполнен по крайней мере 1 раз (Рисунок 11).

Рисунок 11. Цикл do…while.

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

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

Основные понятия

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

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

Рисунок 12. Пример простого класса.

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

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

Рисунок 13. Создание и использование объекта своего класса.

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

В C++ есть особый тип данных, который называется указатели. В отличие от обычных переменных, сами по себе они не хранят данные, но «указывают» на адрес в оперативной памяти, где хранится значение. При использовании указателей доступ к полям объекта производится с помощью оператора -> (Рисунок 14).

Рисунок 14. Использование указателя на объект.

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

Нередко при создании объекта нужно сразу же сделать какие-то вещи. Например, присвоить значения определенным полям. Для этого используются конструкторы (Рисунок 15).

Рисунок 15. Использование конструктора.

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

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

Рисунок 16. Использование деструктора для освобождения памяти.

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

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

Наследование. Инкапсуляция. Полиморфизм

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

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

Допустим у нас есть класс, описывающий собаку (Рисунок 17).

Рисунок 17. Класс Dog.

Затем приходит заказчик и говорит, что помимо собак, в приложении будут ещё и кошки. Тогда нам нужен соответствующий класс (Рисунок 18).

Рисунок 17. Класс Cat.

Что ж, если на этом всё, то можно так и оставить. Но что, если заказчик приходит и говорит, что ещё у него будут хомячки, морские свинки и хамелеоны. Дублировать почти полностью одинаковые классы очень непрактично и создает трудности в сопровождении. Именно для таких ситуаций придумано наследование (Рисунки 18-19).

Рисунок 18. Родительский класс Pet.

Рисунок 19. Наследование от класса Pet.

Для наследования при определении дочернего класса после двоеточия указывается имя родительского класса. Ключевое слово public перед именем класса Pet позволяет дочерним объектам использовать методы родителя, как если бы они были у них самих (например, dog.sit()). Теперь в каждом классе нам нужно реализовать лишь один отличающийся метод.

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

Рисунок 20. Использование полиморфизма.

Важным моментом является то, что такой подход работает только при использовании указателей. Также, если запустить такой код, то мы увидим, что вызывается метод класса Pet, а не классов Dog и Cat. Так происходит из-за того, что метод getVoice() не объявлен виртуальный. При использовании виртуальных методов, компилятор учитывает тип объекта, а не тип указателя, при вызове метода.

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

В C++ для указания доступности используются ключевые слова private, protected и public.

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

Защищенные поля и методы (protected) доступны внутри самого класса и в его наследниках. Публичные поля и методы (public) доступны для использования в любых других классах.

Считается хорошей практикой все поля помечать как private и по умолчанию в C++ они такими и являются, поэтому слово private можно опускать. Чтобы всё же иметь к ним доступ, используются методы, называемые «геттеры» (getter) и «сеттеры» (setter). Обычно они начинаются со слов get и set и, соответственно, возвращают или устанавливают какое-либо поле. При этом можно проделать с ним дополнительные манипуляции. Нередко используют только метод получения, получая таким образом поле только для чтения (Рисунок 21).

Рисунок 21. Свойство только для чтения.

Интерфейсы. Абстрактные классы

C++ как и многие другие языки программирования позволяет создавать интерфейсы – специальные конструкции, от которых можно наследоваться (Рисунок 22).

Рисунок 22. Пример интерфейса.

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

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

Рисунок 23. Пример абстрактного класса.

Класс считается абстрактным, если хотя бы один член класса является абстрактным. Для того, чтобы указать, что метод абстрактный используется конструкция = 0 и отсутствуют фигурные скобки. Также обязательно должно быть указано ключевое слово virtual.

2.4 Заголовочные файлы

Классы зачастую могут быть достаточно объемными. Несколько десятков методов на десятки строк не являются исключительной ситуацией. Чтобы облегчить чтение такого класса разделяют объявление методов и их определение, используя заголовочные файлы. Обычно, заголовочный файл имеет расширение h, а файлы с определением расширение cpp. Заголовочный файл гораздо короче, чем файл с определением (Рисунок 24).

Рисунок 24. Файл Date.h.

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

Рисунок 25. Фрагмент файла Date.cpp.

Методы определяются с помощью оператора :: и при этом должны совпадать со своим объявлением. То есть если в объявлении метод должен вернуть int, то и в определении он должен сделать тоже самое.

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

2.5 Перегрузка методов и операторов

Полиморфизм в C++ проявляется не только на уровне классов, но и на уровне методов. Так, например, мы можем сделать четыре разных конструктора для класса даты (Рисунок 26).

Рисунок 26. Перегрузка конструктора.

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

Для пользовательских типов можно перегружать также операторы, такие как сложение (+), умножение (*), больше (>), равно (==) и другие (Рисунок 27).

Рисунок 27. Перегрузка оператора сложения.

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

2.6 Ключевое слово static

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

Рисунок 28. Использование статичных методов.

Статичные методы можно вызывать через оператор ::, как при их определении.

Это не единственное использование данного ключевого слова. Его можно использовать внутри методов и функций при создании объектов (Рисунок 29).

Рисунок 29. Использование слова static внутри функции.

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

2.7 Пространства имен

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

Рисунок 30. Создание пространства имен.

Пространство имен позволяет просто именовать определенный набор классов и функций. Одним из самых известных пространств имен является std, где находятся многие стандартные классы и функции C++. Определив свое пространство имен, его можно использовать, чтобы указать какой именно класс Date предполагается использовать (Рисунок 31).

Рисунок 31. Использование пространства имен.

Конструкция вида пространство имен::имя класса называется полностью квалифицированным именем класса. Если нужно одновременно использовать несколько классов с одинаковым названием, можно воспользоваться псевдонимами классов (Рисунок 32).

Рисунок 32. Использование псевдонима класса.

Глава 3. Разработка консольного приложения

В качестве приложение была выбрана небольшая консольная игра. Цель игры продержаться как можно большее количество ходов. В начале игрок выбирает за какого персонажа он будет играть (Рисунок 33).

Рисунок 33. Выбор персонажа.

За каждого персонажа отвечает свой класс, которые наследуется от базового класса Character. После выбора, в консоль выводится текущее состояние персонажа, а также подсказка по игровому процессу (Рисунок 34).

Рисунок 34. Состояние персонажа и подсказка.

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

Максимальное значение каждого параметра равно 100. Если значение голода, жажды или скуки падает до 0, игра заканчивается и на экран выводится соответствующее сообщение, в зависимости от параметра. Если значение усталости падает до 0, персонаж автоматически засыпает, как если бы игрок вел команду sleep.

Листинги исходного кода приведены в приложении 1.

Заключение

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

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

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

Список использованной литературы

Источники на русском языке:

  1. Страуструп, Бьярне. Программирование: принципы и практика с использованием С++, 2-е изд. : Пер. с англ. - М. : ООО "И.Д. Вильямс", 201 6. - 1328 с. :ил. - Парал. тит. англ. ISBN 978-5-8459-1949-6 (рус.)
  2. Липпман С., Лажойе Ж., Му Б. Язык программирования C++. Базовый курс, 5-у изд.: Пер. с англ. – М.: ООО «И.Д. Вильямс», 2014. – 1120 с.: ил. - Парал. тит. англ. ISBN 978-5-8459-1839-0 (рус.)

Источники на английском языке:

  1. Jon Kalb, Gašper Ažman. C++ Today: The Beast Is Back. O'Reilly Media (May 2015) ISBN 978-1491931660

Электронные ресурсы:

  1. Справочник по языку C++ https://msdn.microsoft.com/ru-ru/library/3bstk3k5.aspx (дата обращения 28.11.18)
  2. Фундаментальные типы https://ru.cppreference.com/w/cpp/language/types (дата обращения 28.11.18)
  3. Классы в С++ http://cppstudio.com/post/439/ (дата обращения 29.11.18)
  4. Static: Многоцелевое ключевое слово http://cppstudio.com/post/3298/ (дата обращения 05.12.18)

Приложение 1. Листинги исходного кода игры

Файл Character.h:

#include "pch.h"

#include <string>

#pragma once

using namespace std;

class Character

{

private:

int hunger = 50;

int drought = 50;

int boring = 50;

int wear = 100;

bool alive = true;

protected:

int hungerSpeed;

int droughtSpeed;

int boringSpeed;

int wearSpeed;

int timePointsPerDay;

int timePoints;

virtual void eat();

virtual void drink();

virtual void play();

virtual void sleep();

virtual void spentTime(int time);

virtual void spentParams();

virtual bool checkTime(int time);

virtual void showTimeError();

virtual void checkDeath();

public:

Character();

~Character();

virtual void action(string action_name);

virtual void showInfo();

virtual void processTime(int time);

virtual bool isAlive();

};

Файл Character.cpp:

#include "pch.h"

#include "Character.h"

#include <iostream>

using namespace std;

Character::Character()

{

timePoints = timePointsPerDay;

}

Character::~Character()

{

}

void Character::action(string action_name) {

if (action_name == "eat") {

if (checkTime(hungerSpeed)) {

processTime(hungerSpeed);

eat();

}

else {

showTimeError();

}

}

else if (action_name == "drink") {

if (checkTime(droughtSpeed)) {

processTime(droughtSpeed);

drink();

}

else {

showTimeError();

}

}

else if (action_name == "play") {

if (checkTime(boringSpeed)) {

processTime(boringSpeed);

play();

}

else {

showTimeError();

}

}

else if (action_name == "sleep") {

processTime(wearSpeed);

sleep();

}

else {

cout << "Неверное действие. Пожалуйста, используйте eat, dring, play или sleep \n";

}

if(alive)

showInfo();

}

void Character::eat() {

hunger += 100 / hungerSpeed;

if (hunger > 100) hunger = 100;

}

void Character::drink() {

drought += 100 / droughtSpeed;

if (drought > 100) drought = 100;

}

void Character::play() {

boring += 100 / boringSpeed;

if (boring > 100) boring = 100;

}

void Character::sleep() {

wear = 100;

timePoints = timePointsPerDay;

}

void Character::processTime(int time) {

spentTime(time);

spentParams();

}

void Character::spentTime(int time) {

timePoints -= time;

}

void Character::spentParams() {

hunger -= hungerSpeed;

drought -= droughtSpeed;

boring -= boringSpeed;

wear -= wearSpeed;

checkDeath();

}

bool Character::checkTime(int time) {

return timePoints > time;

}

void Character::showTimeError() {

cout << "Вам нужно поспать \n";

}

void Character::showInfo() {

cout << "Очки голода: " << hunger << "\n";

cout << "Очки жажды: " << drought << "\n";

cout << "Очки скуки: " << boring << "\n";

cout << "Очки усталости: " << wear << "\n";

cout << "Очки действий: " << timePoints << "\n";

}

void Character::checkDeath() {

if (hunger <= 0) {

cout << "Вы умерли от голода \n";

alive = false;

}

else if (drought <= 0) {

cout << "Вы умерли от жажды \n";

alive = false;

}

else if (boring <= 0) {

cout << "Вы умерли от скуки \n";

alive = false;

}

else if (wear <= 0) {

cout << "Вы уснули \n";

sleep();

}

}

bool Character::isAlive() {

return alive;

}

Файл Cat.h:

#include "Character.h"

class Cat :

public Character

{

public:

Cat();

~Cat();

};

Файл Cat.cpp:

#include "pch.h"

#include "Cat.h"

Cat::Cat()

{

hungerSpeed = 6;

droughtSpeed = 2;

boringSpeed = 6;

wearSpeed = 6;

timePointsPerDay = 50;

timePoints = timePointsPerDay;

}

Cat::~Cat()

{

}

Файл Dog.h:

#pragma once

#include "Character.h"

class Dog :

public Character

{

public:

Dog();

~Dog();

};

Файл Dog.cpp:

#include "pch.h"

#include "Dog.h"

Dog::Dog()

{

hungerSpeed = 2;

droughtSpeed = 4;

boringSpeed = 7;

wearSpeed = 9;

timePointsPerDay = 100;

timePoints = timePointsPerDay;

}

Dog::~Dog()

{

}

Файл SynergyGame.cpp (точка входа):

#include "pch.h"

#include <iostream>

#include <string>

#include "Character.h"

#include "Cat.h"

#include "Dog.h"

using namespace std;

Character* getCharacter() {

string characterType;

cin >> characterType;

if (characterType == "cat") {

return new Cat();

}

else if (characterType == "dog") {

return new Dog();

}

else {

cout << "Нет такого персонажа!";

return getCharacter();

}

}

int main()

{

setlocale(LC_ALL, "RUS");

cout << "Выберите персонажа (cat или dog)\n";

Character* character = getCharacter();

cout << "Выберите действие (eat, dring, play или sleep)\n";

cout << "sleep завершает текущий день. Все очки сгорают. \n";

string action;

character->showInfo();

while (cin >> action && character->isAlive()) {

character->action(action);

}

return 0;

}