Урок №113. Классы, Объекты и Методы

  Юрий  | 

  |

  Обновл. 20 Дек 2022  | 

 222421

 ǀ   19 

Хотя язык C++ предоставляет ряд фундаментальных типов данных (например, char, int, long, float, double и т.д.), которых бывает достаточно для решения относительно простых проблем, для решения сложных проблем функционала этих простых типов может не хватать.

Классы

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

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

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



Результат выполнения программы:

18/11/2018

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

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

Единственным существенным отличием здесь является public — ключевое слово в классе (о нем мы поговорим детально на соответствующем уроке).

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

В языке C++ переменная класса называется экземпляром (или «объектом») класса. Точно так же, как определение переменной фундаментального типа данных (например, int x) приводит к выделению памяти для этой переменной, так же и создание объекта класса (например, DateClass today) приводит к выделению памяти для этого объекта.

Методы классов


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

Класс Date с методом вывода даты:

Точно так же, как к членам структуры, так и к членам (переменным и функциям) класса доступ осуществляется через оператор выбора членов (.):

Результат выполнения программы:

18/11/2018

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

Методы класса работают несколько иначе: все вызовы методов должны быть связаны с объектом класса. Когда мы вызываем today.print(), то мы сообщаем компилятору вызвать метод print() объекта today.

Рассмотрим определение метода print() еще раз:

На что фактически ссылаются m_day, m_month и m_year? Они ссылаются на связанный объект today (который определен caller-ом).

Поэтому, при вызове today.print(), компилятор интерпретирует:

   m_day, как today.m_day;

   m_month, как today.m_month;

   m_year, как today.m_year.

Если бы мы вызвали tomorrow.print(), то m_day ссылался бы на tomorrow.m_day.

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

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

Использование префикса m_ (англ. «m» = «members») для переменных-членов помогает различать переменные-члены от параметров функции или локальных переменных внутри методов класса. Это полезно по нескольким причинам:

   во-первых, когда мы видим переменную с префиксом m_, то мы понимаем, что работаем с переменной-членом класса;

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

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

Правило: Пишите имена классов с заглавной буквы.

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

Результат выполнения программы:

Name: John
Id: 5
Wage: $30

Name: Max
Id: 6
Wage: $32.75

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

Примечание о структурах в C++

В языке Cи структуры могут только хранить данные и не могут иметь связанных методов. После проектирования классов (используя ключевое слово class) в языке С++, Бьёрн Страуструп размышлял о том, нужно ли, чтобы структуры (которые были унаследованы из языка Си) имели связанные методы. После некоторых размышлений он решил, что нужно. Поэтому в программах, приведенных выше, мы также можем использовать ключевое слово struct, вместо class, и всё будет работать!

Многие разработчики (включая и меня) считают, что это было неправильное решение, поскольку оно может привести к проблемам, например, справедливо предположить, что класс выполнит очистку памяти после себя (например, класс, которому выделена память, освободит её непосредственно перед моментом уничтожения самого класса), но предполагать то же самое при работе со структурами — небезопасно. Следовательно, рекомендуется использовать ключевое слово struct для структур, используемых только для хранения данных, и ключевое слово class для определения объектов, которые требуют объединения как данных, так и функций.

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

Заключение


Оказывается, Стандартная библиотека C++ полна классов, созданных для нашего удобства. std::string, std::vector и std::array — это всё типы классов! Поэтому, когда вы создаете объект любого из этих типов, вы создаете объект класса. А когда вы вызываете функцию с использованием этих объектов, вы вызываете метод:

Ключевое слово class позволяет создать пользовательский тип данных в языке C++, который может содержать как переменные-члены, так и методы. Классы — это основа объектно-ориентированного программирования!

Примечание: Также Вы можете приобрести Самоучитель «Уроки по С++» в .pdf-формате.

Тест

Задание №1

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

   метод set(), который позволит присваивать значения переменным;

   метод print(), который будет выводить значения переменных.

Выполнение следующей функции main():

Должно выдавать следующий результат:

Numbers(3, 3)
Numbers(4, 4)

Ответ №1

Задание №2

Почему для Numbers должен использоваться класс, а не структура?

Ответ №2

Класс Numbers содержит как переменные-члены, так и методы, поэтому мы должны использовать класс. Мы не должны использовать структуры с объектами, которые имеют методы.


Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (408 оценок, среднее: 4,91 из 5)
Загрузка...

Комментариев: 19

  1. Александр:

    как я понял для всех это легко, а мне приятно когда получается

    1. Ivan:

      Но зачем return в методе set(), если это метод внутри класса? Ему ничего не нужно возвращать, так как он работает с переменными внутри класса и ничего возвращать просто нет никакой необходимости.

  2. Vitalt1158:

    А в каком файле/файлах описан например класс string?

  3. Роман:

    Почему в функцию передается адрес структуры &date, а не просто имя? Разве имя сущности структуры не есть указатель на нее?

    1. Mik:

      date — это переменная-ссылка.

    2. DarkMatter:

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

      Если же вы передаёте адрес структуры в качестве параметра, то функция будет вызвана с соответствующим аргументом: f(&some_struct_name). В этом случае копируется адрес структуры, а не сама структура.

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

  4. Александр:

    В ответе int m_first и m_second объявлены как public. Поэтому их можно вызывать как члены структур — cout<<n1.m_first; , необязательно использовать интерфейс класса set и print. Эти методы класса нужны, если int m_first и m_second объявить как privat. Думаю, это важно. И надо напомнить, что Numbers n2{ 4, 4 }; работает для С++2011, для С++2008 (как у меня) надо писать конструктор в public-части класса:

    Иначе компилятор ругается. Новичку будет трудно понять почему .

  5. Алексей:

    Очень удобная фига языка.

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

  6. Виталий:

    Что за новое объявление объекта класса? Какие нафиг фигурные скобки? Так классы не инициализируются! Это что — структура? Конструктор не описан! И он же вызывается круглыми скобками!!! Чушь.

    1. Анонимус:

      Новый стандарт читать надо

      1. Виталий:

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

      2. Виталий:

        И при этом в новом стандарте написано, что должен быть обязательно описан конструктор класса:

        1. Андрей:

          Виталий, спасибо!

        2. Дмитрий:

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

  7. Дмитрий:

    "В C++, переменная класса называется экземпляром или объектом класса. Точно так же, как определение переменной фундаментального типа данных (например, int x) приводит к выделению памяти для этой переменной, так же и создание объекта класса (например, DateClass today) приводит к выделению памяти для этого объекта."- сказано технически правильно но непонятно. Как понятно-почитайте любое техническое пособие до 1970 года. или медицинское.Имеется ввиду стиль изложения материала а не содержание.

  8. Александр:

    В современном C++ class и struct различаются исключительно дефолтным состоянием private (для class) и public (для struct)

    В итоге выбор "что именно использовать" носит чисто семантический характер и рекомендаций (а также обоснований этих рекомендаций) огромное количество.

    В шаблонах можно указывать typename или class, а потом конкретизировать шаблон хоть классом, хоть структурой, хоть базовым типом — разницы не будет.

    Мне кажется, что для новичков до той поры, пока они не столкнутся серьезно с инкапсуляцией, лучше пользоваться только структурами.
    А уже с опытом переходить к "семантическому разделению"
    Но это только мое мнение…

  9. Виктор:

    Здравствуйте.
    Вопрос состоит в следующем:

    Почему при создании объекта мы используем {}, и как происходит инициализация эти значений которые находится в скобках фигурных??

    1. Анонимус:

      В новой стандарте ввели возможность инициализировать классы как инициализировались структуры в языке Си (без ++)

  10. Фото аватара Юрий:

    Согласен, исправил. Спасибо.

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *