Классы

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

Данные-члены
Данные-члены — это те члены, которые содержат данные класса — поля, константы, события. Данные-члены могут быть статическими (static). Член класса является членом экземпляра, если только он не объявлен явно как static. Давайте рассмотрим виды этих данных:
Поля (field)
Это любые переменные, ассоциированные с классом.
Константы
Константы могут быть ассоциированы с классом тем же способом, что и переменные. Константа объявляется с помощью ключевого слова const. Если она объявлена как public, то в этом случае становится доступной извне класса.
События
Это члены класса, позволяющие объекту уведомлять вызывающий код о том, что случилось нечто достойное упоминания, например, изменение свойства класса либо некоторое взаимодействие с пользователем. Клиент может иметь код, известный как обработчик событий, реагирующий на них.
Функции-члены
Функции-члены — это члены, которые обеспечивают некоторую функциональность для манипулирования данными класса. Они включают методы, свойства, конструкторы, финализаторы, операции и индексаторы:
Методы (method)
Это функции, ассоциированные с определенным классом. Как и данные-члены, по умолчанию они являются членами экземпляра. Они могут быть объявлены статическими с помощью модификатора static.
Свойства (property)
Это наборы функций, которые могут быть доступны клиенту таким же способом, как общедоступные поля класса. В C# предусмотрен специальный синтаксис для реализации чтения и записи свойств для классов, поэтому писать собственные методы с именами, начинающимися на Set и Get, не понадобится. Поскольку не существует какого-то отдельного синтаксиса для свойств, который отличал бы их от нормальных функций, создается иллюзия объектов как реальных сущностей, предоставляемых клиентскому коду.
Конструкторы (constructor)
Это специальные функции, вызываемые автоматически при инициализации объекта. Их имена совпадают с именами классов, которым они принадлежат, и они не имеют типа возврата. Конструкторы полезны для инициализации полей класса.
Финализаторы (finalizer)
Вызываются, когда среда CLR определяет, что объект больше не нужен. Они имеют то же имя, что и класс, но с предшествующим символом тильды. Предсказать точно, когда будет вызван финализатор, невозможно.
Операции (operator)
Это простейшие действия вроде + или -. Когда вы складываете два целых числа, то, строго говоря, применяете операцию + к целым. Однако C# позволяет указать, как существующие операции будут работать с пользовательскими классами (так называемая перегрузка операции).
Индексаторы (indexer)
Позволяют индексировать объекты таким же способом, как массив или коллекцию.
Класс создается с помощью ключевого слова class. Ниже приведена общая форма определения простого класса, содержащая только переменные экземпляра и методы:
class имя_класса < // Объявление переменных экземпляра. доступ тип переменная1; доступ тип переменная2; //. доступ тип переменнаяN; // Объявление методов. доступ возращаемый_тип метод1 (параметры) < // тело метода >доступ возращаемый_тип метод2 (параметры) < // тело метода >//. . . доступ возращаемый_тип методN(параметры) < // тело метода >>
Обратите внимание на то, что перед каждым объявлением переменной и метода указывается доступ. Это спецификатор доступа, например public, определяющий порядок доступа к данному члену класса. Члены класса могут быть как закрытыми (private) в пределах класса, так открытыми (public), т.е. более доступными. Спецификатор доступа определяет тип разрешенного доступа. Указывать спецификатор доступа не обязательно, но если он отсутствует, то объявляемый член считается закрытым в пределах класса. Члены с закрытым доступом могут использоваться только другими членами их класса.
Давайте разберем пример создания класса, описывающего характеристики пользователя:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 < class UserInfo < // Поля класса public string Name, Family, Adress; public byte Age; // Метод, выводящий в консоль контактную информацию public void writeInConsoleInfo(string name, string family, string adress, byte age) < Console.WriteLine("Имя: \nФамилия: \nМестонахождение: \nВозраст: \n", name, family, adress, age); > > class Program < static void Main(string[] args) < // Создаем объект типа UserInfo UserInfo myInfo = new UserInfo(); myInfo.Name = "Alexandr"; myInfo.Family = "Erohin"; myInfo.Adress = "ViceCity"; myInfo.Age = 26; // Создадим новый экземпляр класса UserInfo UserInfo myGirlFriendInfo = new UserInfo(); myGirlFriendInfo.Name = "Elena"; myGirlFriendInfo.Family = "Korneeva"; myGirlFriendInfo.Adress = "ViceCity"; myGirlFriendInfo.Age = 22; // Выведем информацию в консоль myInfo.writeInConsoleInfo(myInfo.Name, myInfo.Family, myInfo.Adress, myInfo.Age); myGirlFriendInfo.writeInConsoleInfo(myGirlFriendInfo.Name,myGirlFriendInfo.Family,myGirlFriendInfo.Adress,myGirlFriendInfo.Age); Console.ReadLine(); >> >
В данном примере определяется новый пользовательский класс UserInfo, который содержит 4 поля и 1 метод, которые являются открытыми (т.е. содержат модификатор доступа public). В методе Main() создаются два экземпляра этого класса: myInfo и myGirlFriendInfo. Затем инициализируются поля данных экземпляров и вызывается метод writeInConsoleInfo().
Прежде чем двигаться дальше, рассмотрим следующий основополагающий принцип: у каждого объекта имеются свои копии переменных экземпляра, определенных в его классе. Следовательно, содержимое переменных в одном объекте может отличаться от их содержимого в другом объекте. Между обоими объектами не существует никакой связи, за исключением того факта, что они являются объектами одного и того же типа. Так, если имеются два объекта типа UserInfo, то у каждого из них своя копия переменных Name, Family, Age и Adress, а их содержимое в обоих объектах может отличаться:
Создание экземпляра класса
До сих пор мы работали с уже созданными .NET типами или объектами. Но с отражением мы можем создавать экземпляры и во время выполнения, зная имя класса, который мы хотим создать. Есть несколько способов сделать это, но я предпочитаю получить ссылку на конструктор, который я хочу использовать, вызвать его, а затем использовать возвращенное значение в качестве моего экземпляра. Вот пример того, как это сделать. Сначала код, потом я все объясню:
using System; using System.Collections.Generic; using System.Text; using System.Reflection; namespace ReflectionTest < class Program < static void Main(string[] args) < Type testType = typeof(TestClass); ConstructorInfo ctor = testType.GetConstructor(System.Type.EmptyTypes); if(ctor != null) < object instance = ctor.Invoke(null); MethodInfo methodInfo = testType.GetMethod("TestMethod"); Console.WriteLine(methodInfo.Invoke(instance, new object[] < 10 >)); > Console.ReadKey(); > > public class TestClass < private int testValue = 42; public int TestMethod(int numberToAdd) < return this.testValue + numberToAdd; >> >
Я определил простой класс для тестирования этого, называемый TestClass. Он просто содержит личное поле и открытый метод. Метод возвращает значение частного свойства, к которому добавляется значение параметра. Теперь нам нужно создать новый экземпляр этого TestClass, вызвать TestMethod и вывести результат на консоль.
В этом примере мы можем позволить себе роскошь использовать typeof () непосредственно на TestClass, но в какой-то момент вам, возможно, придется сделать это исключительно с помощью имени нужного класса. В этом случае можно получить ссылку на него через сборку, в которой он объявлен, как показано в главе о типе.
Таким образом, со ссылкой на Тип класса мы запрашиваем конструктор по умолчанию с помощью метода GetConstructor (), передавая System.Тип.EmptyTypes в качестве параметра. В случае, если нам нужен конкретный конструктор, мы должны предоставить массив типов, каждый из которых определяет, какой параметр конструктора мы ищем.
Как только у нас есть ссылка на конструктор, мы просто вызываем метод Invoke () для создания нового экземпляра класса TestClass. Мы передаем null в качестве параметра для вызова (), так как мы не хотим указывать какие-либо параметры. Мы используем GetMethod (), вместе с именем метода, который мы хотим, чтобы получить функцию TestMethod (), а затем мы еще раз используем магию метода Invoke () для вызова этой функции. На этот раз нам нужно указать параметр в виде массива объектов. Мы делаем это на лету, указывая число 10 в качестве единственного параметра, который нам нужен, и затем выводим результат вызова метода. Все это благодаря магии отражения!
This article has been fully translated into the following languages:
Is your preferred language not on the list? Click here to help us translate this article into your language!
#19 – Создание классов и объектов

За урок вы на практике изучите использование классов и объектов. Вы познакомитесь с C++ ООП, научитесь строить классы и объекты на их основе.
Видеоурок
Создание классов
Для создания класса необходимо прописать ключевое слово class и далее название для класса. Общепринято начинать названия классов с буквы в верхнем регистре, но если этого не сделать, то ошибки не будет.
В любом классе можно создавать поля (переменные), методы (функции), а также конструкторы.
Создав новый класс и поместив туда какую-либо информацию мы можем создавать на основе него новые объекты. Объекты будут иметь доступ ко всем характеристикам класса, которые отмечены модификатором public .
Существует три модификатора доступа:
- public — данные будут видны повсюду, как в классе, так и вне его;
- protected — данные будут видны только в классе, где они были созданы, а также в классах наследниках;
- private — данные будут видны только в классе, где они были созданы.
Пример простого класса приведен ниже:
class Book < public: int pages; char name; float weight; void getInfoBook () < cout >;
На основе такого класса мы можем создать множество объектов. Каждый объект в данном случае будет представлять из себя конкретную книжку. Для каждого объекта мы можем указать уникальные данные: количество страниц, название книги и её вес.
Чтобы создать объект нам потребуется следующий код:
Book sherlock_holms; // Создание объекта sherlock_holms.getInfoBook(); // Вызов метода класса
Такой код можно прописать и в одной строке кода. Мы для наглядности разбили код в две строки.
Чтобы брать данные из класса через объект необходимо ставить точку и указывать имя переменной или функции, которую мы хотим взять.
Создание классов
#include #include using namespace std; class Building < private: int year; string type; public: void set_data(int y, string t) < year = y; type = t; >void get_info() < cout >; int main() < setlocale(LC_ALL, "RU"); Building school; //school.type = "Школа"; //school.year = 2000; school.set_data(2000, "Школа"); school.get_info(); Building house; //house.type = "Дом"; //house.year = 2010; house.set_data(2010, "Дом"); house.get_info(); return 0; >
Задание к уроку
Необходимо оформить подписку на проект, чтобы получить доступ ко всем домашним заданиям
Большое задание по курсу
Вам необходимо оформить подписку на сайте, чтобы иметь доступ ко всем большим заданиям. В задание входит методика решения, а также готовый проект с ответом к заданию.
PS: подобные задания доступны при подписке от 1 месяца
Классы в C++ — урок 10
Весь реальный мир состоит из объектов. Города состоят из районов, в каждом районе есть свои названия улиц, на каждой улице находятся жилые дома, которые также состоят из объектов.
Практически любой материальный предмет можно представить в виде совокупности объектов, из которых он состоит. Допустим, что нам нужно написать программу для учета успеваемости студентов. Можно представить группу студентов, как класс языка C++. Назовем его Students .
- Основные понятия
- Модификаторы доступа public и private
- Программа учета успеваемости студентов
- Отделение данных от логики
- Создание объекта через указатель
- Конструктор и деструктор класса
Основные понятия
Классы в программировании состоят из свойств и методов. Свойства — это любые данные, которыми можно характеризовать объект класса. В нашем случае, объектом класса является студент, а его свойствами — имя, фамилия, оценки и средний балл.
У каждого студента есть имя — name и фамилия last_name . Также, у него есть промежуточные оценки за весь семестр. Эти оценки мы будем записывать в целочисленный массив из пяти элементов. После того, как все пять оценок будут проставлены, определим средний балл успеваемости студента за весь семестр — свойство average_ball .
Методы — это функции, которые могут выполнять какие-либо действия над данными (свойствами) класса. Добавим в наш класс функцию calculate_average_ball() , которая будет определять средний балл успеваемости ученика.
- Методы класса — это его функции.
- Свойства класса — его переменные.
// считаем среднее арифметическое average_ball = sum / 5.0; > // Имя студента std::string name; // Фамилия std::string last_name; // Пять промежуточных оценок студента int scores[5]; private: // Итоговая оценка за семестр float average_ball; >;
Функция calculate_average_ball() просто делит сумму всех промежуточных оценок на их количество.
Модификаторы доступа public и private
Все свойства и методы классов имеют права доступа. По умолчанию, все содержимое класса является доступным для чтения и записи только для него самого. Для того, чтобы разрешить доступ к данным класса извне, используют модификатор доступа public . Все функции и переменные, которые находятся после модификатора public , становятся доступными из всех частей программы.
Закрытые данные класса размещаются после модификатора доступа private . Если отсутствует модификатор public , то все функции и переменные, по умолчанию являются закрытыми (как в первом примере).
Обычно, приватными делают все свойства класса, а публичными — его методы. Все действия с закрытыми свойствами класса реализуются через его методы. Рассмотрим следующий код.
// Получение среднего балла float get_average_ball() < return average_ball; >std::string name; std::string last_name; int scores[5]; private: float average_ball; >;
Мы не можем напрямую обращаться к закрытым данными класса. Работать с этими данными можно только посредством методов этого класса. В примере выше, мы используем функцию get_average_ball() для получения средней оценки студента, и set_average_ball() для выставления этой оценки.
Функция set_average_ball() принимает средний балл в качестве параметра и присваивает его значение закрытой переменной average_ball . Функция get_average_ball() просто возвращает значение этой переменной.
Программа учета успеваемости студентов
Создадим программу, которая будет заниматься учетом успеваемости студентов в группе. Создайте заголовочный файл students.h, в котором будет находиться класс Students .
class Students < public: // Установка имени студента void set_name(std::string student_name) < name = student_name; >// Получение имени студента std::string get_name() < return name; >// Установка фамилии студента void set_last_name(std::string student_last_name) < last_name = student_last_name; >// Получение фамилии студента std::string get_last_name() < return last_name; >// Установка промежуточных оценок void set_scores(int student_scores[]) < for (int i = 0; i < 5; ++i) < scores[i] = student_scores[i]; >> // Установка среднего балла void set_average_ball(float ball) < average_ball = ball; >// Получение среднего балла float get_average_ball() < return average_ball; >private: // Промежуточные оценки int scores[5]; // Средний балл float average_ball; // Имя std::string name; // Фамилия std::string last_name; >;
Мы добавили в наш класс новые методы, а также сделали приватными все его свойства. Функция set_name() сохраняет имя студента в переменной name , а get_name() возвращает значение этой переменной. Принцип работы функций set_last_name() и get_last_name() аналогичен.
Функция set_scores() принимает массив с промежуточными оценками и сохраняет их в приватную переменную int scores[5] .
Теперь создайте файл main.cpp со следующим содержимым.
#include "students.h" int main() < // Создание объекта класса Student Students student; std::string name; std::string last_name; // Ввод имени с клавиатуры std::cout > scores[i]; // суммирование sum += scores[i]; > // Сохраняем промежуточные оценки в объект класса Student student.set_scores(scores); // Считаем средний балл float average_ball = sum / 5.0; // Сохраняем средний балл в объект класса Students student.set_average_ball(average_ball); // Выводим данные по студенту std::cout
В самом начале программы создается объект класса Students . Дело в том, что сам класс является только описанием его объекта. Класс Students является описанием любого из студентов, у которого есть имя, фамилия и возможность получения оценок.
Объект класса Students характеризует конкретного студента. Если мы захотим выставить оценки всем ученикам в группе, то будем создавать новый объект для каждого из них. Использование классов очень хорошо подходит для описания объектов реального мира.
После создания объекта student , мы вводим с клавиатуры фамилию, имя и промежуточные оценки для конкретного ученика. Пускай это будет Вася Пупкин, у которого есть пять оценок за семестр — две тройки, две четверки и одна пятерка.
Введенные данные мы передаем set-функциям, которые присваивают их закрытым переменным класса. После того, как были введены промежуточные оценки, мы высчитываем средний балл на основе этих оценок, а затем сохраняем это значение в закрытом свойстве average_ball , с помощью функции set_average_ball() .
Скомпилируйте и запустите программу.
Отделение данных от логики
Вынесем реализацию всех методов класса в отдельный файл students.cpp.
#include "students.h" // Установка имени студента void Students::set_name(std::string student_name) < Students::name = student_name; >// Получение имени студента std::string Students::get_name() < return Students::name; >// Установка фамилии студента void Students::set_last_name(std::string student_last_name) < Students::last_name = student_last_name; >// Получение фамилии студента std::string Students::get_last_name() < return Students::last_name; >// Установка промежуточных оценок void Students::set_scores(int scores[]) < for (int i = 0; i < 5; ++i) < Students::scores[i] = scores[i]; >> // Установка среднего балла void Students::set_average_ball(float ball) < Students::average_ball = ball; >// Получение среднего балла float Students::get_average_ball()
А в заголовочном файле students.h оставим только прототипы этих методов.
class Students < public: // Установка имени студента void set_name(std::string); // Получение имени студента std::string get_name(); // Установка фамилии студента void set_last_name(std::string); // Получение фамилии студента std::string get_last_name(); // Установка промежуточных оценок void set_scores(int []); // Установка среднего балла void set_average_ball(float); // Получение среднего балла float get_average_ball(); private: // Промежуточные оценки int scores[5]; // Средний балл float average_ball; // Имя std::string name; // Фамилия std::string last_name; >;
Такой подход называется абстракцией данных — одного из фундаментальных принципов объектно-ориентированного программирования. К примеру, если кто-то другой захочет использовать наш класс в своем коде, ему не обязательно знать, как именно высчитывается средний балл. Он просто будет использовать функцию calculate_average_ball() из второго примера, не вникая в алгоритм ее работы.
Над крупными проектами обычно работает несколько программистов. Каждый из них занимается написанием определенной части продукта. В таких масштабах кода, одному человеку практически нереально запомнить, как работает каждая из внутренних функций проекта. В нашей программе, мы используем оператор потокового вывода cout , не задумываясь о том, как он реализован на низком уровне. Кроме того, отделение данных от логики является хорошим тоном программирования.
В начале обучения мы говорили о пространствах имен (namespaces). Каждый класс в C++ использует свое пространство имен. Это сделано для того, чтобы избежать конфликтов при именовании переменных и функций. В файле students.cpp мы используем оператор принадлежности :: перед именем каждой функции. Это делается для того, чтобы указать компилятору, что эти функции принадлежат классу Students .
Создание объекта через указатель
При создании объекта, лучше не копировать память для него, а выделять ее в в куче с помощью указателя. И освобождать ее после того, как мы закончили работу с объектом. Реализуем это в нашей программе, немного изменив содержимое файла main.cpp.
#include "students.h" int main() < // Выделение памяти для объекта Students Students *student = new Students; std::string name; std::string last_name; // Ввод имени с клавиатуры std::cout set_name(name); student->set_last_name(last_name); // Оценки int scores[5]; // Сумма всех оценок int sum = 0; // Ввод промежуточных оценок for (int i = 0; i < 5; ++i) < std::cout > scores[i]; // суммирование sum += scores[i]; > // Сохраняем промежуточные оценки в объект класса Student student->set_scores(scores); // Считаем средний балл float average_ball = sum / 5.0; // Сохраняем средний балл в объект класса Students student->set_average_ball(average_ball); // Выводим данные по студенту std::cout get_name() get_last_name() get_average_ball()
При создании статического объекта, для доступа к его методам и свойствам, используют операция прямого обращения — « . » (символ точки). Если же память для объекта выделяется посредством указателя, то для доступа к его методам и свойствам используется оператор косвенного обращения — « -> ».
Конструктор и деструктор класса
Конструктор класса — это специальная функция, которая автоматически вызывается сразу после создания объекта этого класса. Он не имеет типа возвращаемого значения и должен называться также, как класс, в котором он находится. По умолчанию, заполним двойками массив с промежуточными оценками студента.
> private: int scores[5]; >; int main() < // Передаем двойку в конструктор Students *student = new Students(2); return 0; >
Мы можем исправить двойки, если ученик будет хорошо себя вести, и вовремя сдавать домашние задания. А на «нет» и суда нет 🙂
Деструктор класса вызывается при уничтожении объекта. Имя деструктора аналогично имени конструктора, только в начале ставится знак тильды ~ . Деструктор не имеет входных параметров.
class Students < public: // Деструктор ~Students() < std::cout >; int main() < Students *student = new Students; // Уничтожение объекта delete student; return 0; >