- •Классы, Объекты и Методы
- •Struct DateStruct {
- •Int month;
- •Int year;
- •Методы классов
- •Void print () {
- •Int main () {
- •Int main () {
- •Int main () {
- •Спецификаторы доступа public и private
- •Int year; // открыто по умолчанию
- •Int main () {
- •Int main () {
- •Int main ()
- •Структуры. Классы
- •Инкапсуляция, Геттеры и Сеттеры Инкапсуляция
- •Int m_array[10];
- •Int main () {
- •IntArray array;
- •Void setValue(int index, int value) {
- •Функции доступа (геттеры и сеттеры)
- •Int m_month;
- •Int m_year;
- •Int main () {
- •Краткий обзор l-value и r-value
- •Инициализация ссылок
- •Ссылки в качестве параметров в функциях
- •Void changeN (int &ref) {
- •Int main () {
- •Int value1;
- •Int otherValue;
- •Ссылки. Указатели
- •Конструкторы
- •Int main () {
- •Конструкторы по умолчанию
- •Конструкторы с параметрами
- •Int m_numerator;
- •Int m_denominator;
- •Int getNumerator () {return m_numerator; }
- •Int getDenominator () {return m_denominator; }
- •Копирующая инициализация
- •Уменьшение количества конструкторов
- •Неявно генерируемый конструктор по умолчанию
- •Int main () {
- •Int main () {
- •Классы, содержащие другие классы
- •Int main() {
- •Список инициализации членов класса
- •Списки инициализации членов класса
- •Int m_value1;
- •Values() {
- •Int m_value1;
- •Инициализация массивов в классе
- •Инициализация переменных-членов, которые являются классами
- •Int main() {
- •Использование списков инициализации
- •Int m_value1;
- •Int m_value1;
- •Int m_value1;
- •Порядок выполнения в списке инициализации
- •Инициализация нестатических членов класса
- •Void print() {
- •Int main() {
- •Void print() {
- •Int main() {
- •Void print() {
- •Int main() {
- •Void print() {
- •Int main() {
- •Делегирующие конструкторы
- •Void DoX() {
- •Init();
- •Init();
- •Void Init()
- •Int main() {
- •Ещё о делегирующих конструкторах
- •Деструкторы
- •Имена деструкторов
- •Пример использования деструктора на практике
- •Выполнение конструкторов и деструкторов
- •Int getId() { return m_nId; }
- •Int main() {
- •Предупреждение о функции exit()
- •Скрытый указатель this
- •Скрытый указатель *this
- •Int m_number;
- •Int getNumber() { return m_number; }
- •Int main() {
- •Указатель this всегда указывает на текущий объект. Явное указание указателя this
- •Int data;
- •Цепочки методов класса
- •Заключение
ООП
Так что же тогда является объектно-ориентированным программированием?
Для лучшего понимания воспользуемся аналогией.
Оглянитесь вокруг, везде находятся объекты: книги, здания, еда и даже вы сами.
Объекты имеют два основных компонента:
свойства (например, вес, цвет, размер, прочность, форма и т.д.);
поведение, которое они могут проявлять (например, открывать что-либо, делать что-то и т.д.).
При этом, главное - Свойства и поведение неотделимы друг от друга.
Объектно-ориентированное программирование (сокр. «ООП») предоставляет возможность создавать объекты, которые объединяют свойства и поведение в самостоятельный союз, описывая некоторые объекты предметной области. Этот союз затем можно многоразово использовать.
Вместо того, чтобы сосредоточиться на написании функций, мы концентрируемся на определении объектов, которые имеют четкий набор поведений.
Вот почему эта парадигма называется «объектно-ориентированной».
Это позволяет создавать программы модульным способом, что упрощает не только написание и понимание кода, но и обеспечивает более высокий уровень возможности повторного использования этого кода.
Объекты также обеспечивают более интуитивный способ работы с данными, позволяя программисту определить, как он будет взаимодействовать с объектами, и как эти объекты будут взаимодействовать с другими объектами.
Обратите внимание, ООП не заменяет традиционные методы программирования. ООП — это дополнительный инструмент управления сложностью ПО.
Объектно-ориентированное программирование также предоставляет несколько полезных концепций, таких как наследование, инкапсуляция, абстракция и полиморфизм.
Мы рассмотрим каждую из этих концепций еще раз.
В объектно-ориентированном программировании, «объект» — это тот же объект, что и в традиционном программировании, но который соединяет в себе как свойства, так и способы поведения.
Классы, Объекты и Методы
Хотя язык C++ предоставляет ряд фундаментальных типов данных (например, char, int, long, float, double и т.д.), которых бывает достаточно для решения относительно простых проблем, для решения сложных проблем функционала этих простых типов может не хватать.
Классы
Одной из наиболее полезных особенностей языка C++ является возможность определять собственные типы данных, которые будут лучше соответствовать в решении конкретных проблем.
Вы уже видели, как перечисления и структуры могут использоваться для создания собственных пользовательских типов данных.
Например, структура для хранения даты:
Struct DateStruct {
int day;
Int month;
Int year;
};
Перечисления и структуры в понимании процедурного программирования — это традиционный мир программирования, в котором мы можем только хранить данные.
В C++11 мы можем создать и инициализировать структуру следующим образом:
DateStruct today {12, 11, 2018};
Для вывода даты на экран, что может понадобиться выполнить и не раз, можно будет написать отдельную функцию, например:
#include <iostream>
struct DateStruct {
int day;
int month;
int year;
};
void print (DateStruct &date) {
std::cout << date.day<< "/" << date.month << "/" << date.year;
}
int main () {
// используем uniform-инициализацию
DateStruct today {12, 11, 2018};
today.day = 18;
print (today);
return 0;
}
Результат выполнения программы:
18/11/2018
В объектно-ориентированном программировании типы данных могут содержать не только данные, но и функции, которые будут работать с этими данными.
Для определения такого типа данных в языке C++ используется ключевое слово class.
Использование ключевого слова class определяет новый пользовательский тип данных — класс.
В языке C++ классы очень похожи на структуры, за исключением того, что они обеспечивают гораздо большую мощность и гибкость.
Фактически, следующая структура и класс идентичны по функционалу:
Единственным существенным отличием здесь является public — ключевое слово в классе (о нем мы поговорим детально далее).
struct DateStruct {
int day;
int month;
int year;
};
class DateClass {
public:
int m_day;
int m_month;
int m_year;
};
Так же, как и объявление структуры, объявление класса не приводит к выделению какой-либо памяти.
Для использования класса нужно объявить переменную этого типа класса:
// определяем и инициализируем переменную класса DateClass
DateClass today {12, 11, 2018};
В языке C++ переменная класса называется экземпляром (или «объектом») класса.
Точно так же, как определение переменной фундаментального типа данных (например, int x) приводит к выделению памяти для этой переменной, так же и создание объекта класса (например, DateClass today) приводит к выделению памяти для этого объекта.
Методы классов
Помимо хранения данных, классы могут содержать и функции!
Функции, определенные внутри класса, называются методами.
Методы могут быть определены, как внутри, так и вне класса.
Пока что мы будем определять их внутри класса (для простоты), как определить их вне класса — вспомним несколько позже.
Класс DateClass с методом вывода даты:
class DateClass {
public:
int m_day;
int m_month;
int m_year;
void print () // определяем функцию-член
{
std::cout << m_day << "/" << m_month << "/" << m_year;
}
};
Точно так же, как к членам структуры, так и к членам (переменным и функциям) класса доступ осуществляется через операцию выбора членов (.):
#include <iostream>
class dateClass {
public:
int m_day;
int m_month;
int m_year;
void print () {
std::cout << m_day << "/" << m_month << "/" << m_year;
}
};
int main () {
dateClass today {12, 11, 2018};
today.m_day = 18; // используем операцию выбора членов для //выбора переменной-члена m_day объекта today класса dateClass
today.print (); // используем операцию выбора членов для вызова //метода print () объекта today класса dateClass
return 0;
}
Результат выполнения программы:
18/11/2018
Обратите внимание, как эта программа похожа на вышеприведенную программу, где используется структура.
Однако есть несколько отличий. В версии DateStruct нам нужно было передать переменную структурного типа непосредственно в функцию print () в качестве параметра.
Если бы мы этого не сделали, то функция print () не знала бы, какую переменную типа структуры DateStruct выводить.
Методы класса работают несколько иначе: все вызовы методов должны быть связаны с объектом класса.
Когда мы вызываем today.print (), то мы сообщаем компилятору вызвать метод print () объекта today.
Этот метод связан с объектом!
Рассмотрим определение метода print () еще раз:
void print () // определяем метод
{
std::cout << m_day << "/" << m_month << "/" << m_year;
}
Здесь специально используется префикс m_ (англ. «m» = «members») для переменных-членов класса и помогает различать переменные-члены от параметров функции или локальных переменных внутри методов класса.
Это полезно по нескольким причинам:
во-первых, когда мы видим переменную с префиксом m_, то мы понимаем, что работаем с переменной-членом класса;
во-вторых, в отличие от параметров функции или локальных переменных, объявленных внутри функции, переменные-члены объявляются в определении класса.
Следовательно, если мы хотим знать, где объявлена переменная с префиксом m_, мы понимаем, что искать нужно в определении класса, а не внутри функции.
Обычно программисты пишут имена классов с заглавной буквы.
Вот еще один пример программы с использованием класса:
#include <iostream>
#include <string>
class employee {
public:
std::string m_name;
int m_id;
double m_wage;
// метод вывода информации о работнике на экран
