Классы в C++: руководство для начинающих!
Всем привет! Объекты очень важная вещь в программировании, которая может облегчить решения многих задач. Например нужно вести дневник пользователя: год рождения, имя, фамилия, местожительство, все это можно продолжать еще очень долго. Поэтому, как и другие языки программирования C++ обзавелся - классами.
Как создать класс
Чтобы объявить класс нужно использовать данную конструкцию:
class <имя класса> {
};
Обычно <имя класса>
прописывают с заглавной буквы. Также в конце обязательно должна присутствовать точка с запятой (;
).
Что такое класс
Это абстрактный тип данных. Он сочетает в себе два функционала:
- Первая - это структура, в которой можно хранить различные типы данных: массивы, переменные, функции.
- Вторая - возможность пользоваться объектно-ориентированным программированием (ООП - об этом ниже).
Создав класс можно создать его экземпляр - объект. Объект - это функционирующий прототип класса, которому можно задавать свойства и вызывать методы.
У каждого вами созданного класса могут быть свойства и методы. Свойства - это все что может хранить информацию, которую вы потом можете заполнять (переменные, массивы и т.д.).
Так свойства класса Worker
(рабочий) может иметь - имя, производительность (полезность работы) за 6 месяцев, среднюю производительность.
class Worker {
public: // об этом ниже
string name; // имя
// производительность в течении 6 месяцев
int academic_performance[6];
// средняя производительность
int avarage_AP;
};
Методы - это обычные функции, в функционале которых можно использовать свойства.
class Worker {
public: // об этом ниже
// функция для вычисления средней производительности
void discover_avarage_AP () {
double answer = 0;
for (int i = 0; i < 6; i++) {
answer += academic_performance[i];
}
avarage_AP = answer / 6; // вычисляем с.п.
}
string name; // имя
// производительность в течении 6 месяцев
int academic_performance[6];
// средняя успеваемость
int avarage_AP;
};
Чтобы обратится к свойствам и методам класса нужно перед названием имени свойства поставить точку .
.
<имя класса>.<название свойства или метода>;
Что такое ООП
Раньше программистам приходилось весь функционал программы записывать в одном файле. Что в будущем неизбежно приводило к путанице из-за нескольких сотен и даже тысяч строк. А с приходом классов появилась возможность отделять любую часть программы в отдельный файл.
Например один файл отвечает за инициализацию введенных данных, друг ой за считывание производительности. Таким образом стала возможным структурировать программу.
В ООП входит такие свойства:
-
Инкапсуляция - это возможность задавать разную область видимости определенной части класса .
-
Наследование - это свойство создавать новый класс на базе старого. Такие классы называют потомками, например, есть класс
магазин
, на базе которого можно создать потомкипродуктовый_магазин
,магазин_одежды
(не обращайте внимание, что название на русском языке). -
Полиморфизм - возможность создать объекты с одинаковым интерфейсом, но с разной их реализацией. Например, есть три класса
треугольник
,круг
иквадрат
. У каждого из них есть методSquarePlis()
, который вычисляет площадь фигуры. Но для каждого класса функция реализована по-разному.SquarePlis() { square = a * a; // для квадрата square = 1 / 2 * h * a; // для треугольника square = 3.14 * r * r; // для круга }
-
Абстракция - это возможность выбирать только те свойства или функции, которые нам необходимы. Например, при создании класса про работника понадобится указать его имя, возраст, образование, но никак его цвет волос, глаз, рост и тому подобное.
Спецификаторы доступа public и private
Для разграничение содержимого класса, например которое пользователю лучше не трогать, были добавлены спецификаторы доступа public, private и protected (о нем пойдет речь в следующем уроке про наследование). Это и есть инкапсуляция, которую мы упоминали выше.
-
public
- дает публичный доступ, содержимому, которое в нем указано. Так можно обратится к любой переменной или функции из любой части программы. -
private
- запрещает обращаться к свойствам вне класса. Поэтому под крылом этого доступа часто находятся именно объявления переменных, массивов, а также прототипов функций.Оперировать его содержимым можно только из методов класса. Получается все преобразования: добавление, вычитание нужно будет делать в функции (их еще называют set и get функциями).class Worker { public: void discover_avarage_AP () { // ... set_avarage_AP(answer); // вместо avarage_AP = answer / 6; } void set_avarage_AP (double score) { avarage_AP = score / 6; } double get_avarage_AP () { return avarage_AP; } private: string name; // имя // успеваемость за 6 месяцев int academic_performance[6]; // средняя успеваемость int avarage_AP; };
-
В строке 5: используем функцию
set_avarage_AP()
для установки нового значенияavarage_AP
. -
В строке 13: находится функция
get_avarage_AP()
, которая передает значенияavarage_AP
.
Если отсутствуют права доступа, то по умолчанию будет стоять доступ
private
.
Размещать права доступа можно в разном порядке, но лучше сделать свою систему которой вы будете придерживаться в будущих проектах.
Функции get и set классов
При создании класса обычно создают функции в названии которых присутствуют слова set
и get
.
-
set
- в ней инициализируют свойства класса.set_number() { // set и имя переменной cin >> number; // которую инициализируют } private: int number; // наша переменная
-
get
- выводит свойства конечному пользователю.get_number() { // get и переменная return number; // которую возвращают } private: int number; // наша переменная
Пример использования классов
Давайте используем созданный класс на практике создав карточку об одном работнике, например Иване. Класс разместим в файле workers.h
, который подключим к главному файлу main.cpp
таким образом #include "workers.h"
.
// workers.h
#include <string>
using namespace std;
class Worker {
public:
void discover_avarage_AP () {
double answer = 0;
for (int i = 0; i < 6; i++) {
answer += academic_performance[i];
}
set_avarage_AP(answer);
// вместо avarage_AP = answer / 6;
}
void set_avarage_AP (double score) {
avarage_AP = score / 6;
}
// здесь находятся set и get функции
double get_avarage_AP () {
return avarage_AP;
}
void set_name(string a) {
// считываем имя
name = a;
}
void set_academic_performance (vector v) {
// заполняем 6 месячныю успеваемость
for (int i = 0; i < 6; i++) { academic_performance[i] = v[i];
}
}
string get_name () {
// выводим имя
return name;
}
// конец set и get функций
private:
// средняя успеваемость
int avarage_AP;
string name; // имя
// успеваемость за 6 месяцев
int academic_performance[6];
};
В строках 19-34: находятся set
и get
функции для инициализации наших свойств. Вот какие именно:
get_name()
- считывает имя работника.get_academic_perfomence()
- считывает успеваемость на работе за шесть месяцев.
Функции set имеют такое же название, только вместо get - set.
А вот как выглядит main.cpp
// main.cpp
#include <iostream>
#include <vector>
#include "workers.h"
using namespace std;
int main() {
Worker employee;
string name;
vector <int> average_balls;
cout << "Your name: "; cin >> name;
cout << "Your academic performance for 6 months: " << endl;
for (int i = 0; i < 6; i++) {
int one_score;
cout << i + 1 << ") "; cin >> one_score;
average_balls.push_back(one_score);
}
employee.set_name(name);
employee.set_academic_performance(average_balls);
employee.discover_avarage_AP();
cout << endl << employee.get_name() << endl;
cout << "Avarage academic performance: " << employee.get_avarage_AP() << endl;
return 0;
}
Для создания объекта employee
мы указали класс Worker
.
- В строках 14 - 21: считываем пользовательские данные.
- В строках 23- 24: отсылаем полученные данные классу (функциями set).
- Вычисляем среднюю успеваемость вызвав функцию
discover_avarage_AP()
в строке 26. - В строках 28 - 29: выводим все свойства: имя, фамилию, возраст, средний балл.
employee
это журнал всего лишь для одного работника, если нам потребуется вести такие журналы всей компании, то придется создать для каждого человека свой объект.
Your name: Иван
Your academic performance for 6 months:
1) 3
2) 4
3) 5
4) 5
5) 3
6) 4
Иван
Avarage academic performance: 4
Process returned 0 (0x0) execution time : 0.010 s
Press any key to continue.
Как создать массив объектов
Кроме создания единичного объекта класса можно создать целый массив объектов. Нужно всего лишь в конец создаваемого объекта добавить его количество ([n]
).
Worker employee[5];
cout << "Name for second employee : "; cin >> name;
employee[2].set_name(name);
Здесь мы просим пользователя ввести имя для второго работника. Далее вводим введенное имя в объект employee[2]
с помощью функции set_name()
.
Вынесение методов от логики
Давайте отделим реализацию всех методов отдельный файл.
// main.cpp
#include <iostream>
#include "Worker.h"
void Worker::discover_avarage_AP () {
double answer = 0;
for (int i = 0; i < 6; i++) {
answer += academic_performance[i];
}
set_avarage_AP(answer);
}
void Worker::set_avarage_AP (double score) {
avarage_AP = score / 6;
}
// set - get функции
double Worker::get_avarage_AP () {
return avarage_AP;
}
void Worker::set_name(string a) {
// считываем имя
name = a;
}
void Worker::set_academic_performance (vector v) {
// заполняем 6 месячныю успеваемость
for (int i = 0; i < 6; i++) {
academic_performance[i] = v[i];
}
}
string Worker::get_name () {
// выводим имя
return name;
}
А вот файл с логикой класса.
class Worker {
public:
// высчитывание среднего балла
void discover_avarage_AP ();
void set_avarage_AP (double score);
// вывод средней успеваемости
double get_avarage_AP ();
// получение и вывод имени
void set_name(string a);
string get_name ();
// получение баллов за шесть месяцев
void set_academic_performance (vector v);
private:
// средняя успеваемость
int avarage_AP;
string name; // имя
// успеваемость за 6 месяцев
int academic_performance[6];
};
Только что мы применили один из фундаментальных принципов объектно ориентированного программирования - абстракция данных. Если ваш класс будут применять в своих целях, им не нужно знать, как реализована какая-то в нем функция. Например, можно для вычисления средней успеваемости применить функцию discover_avarage_AP()
, и даже не вдаваться в ее принцип работы.
В одной из прошлых статей мы разбирали работу пространства имен - namespace. Когда работают несколько программистов над проектом часто случается, что один программист не знает, что уже создан класс или функция с таким именем. Именно в таких случаях помогает префикс ::
который сигнализирует, что эта функция или переменная принадлежит именно к этому классу.
Поэтому перед каждой функцией стоит данный префикс.
Инициализация объектов с помощью указателей
Когда обычным образом создаем объект мы копируем память компьютера, что не есть хорошо. Поэтому лучше выделять память в куче с помощью указателя. Если уж быть вообще правильным то в конце программы нужно удалять каждый созданный объект.
Чтобы создать объект с помощью указателя необходимо использовать конструкцию ниже:
<название класса> *<имя объекта> = new <название класса>
Для освобождения памяти используется оператор delete
:
delete <название объекта>;
При обращении к свойствам и методам объекта применяется такая конструкция (->
):
<имя объекта>-><название свойства или метода>
Давайте используем данную реализацию в нашей программе.
// main.cpp
#include <iostream>
#include <vector>
#include workers.h
using namespace std;
int main() {
Worker *employee = new Worker;
string name;
vector <int> average_balls;
cout << "Your name: "; cin >> name;
cout << "Your academic performance for 6 months: " << endl;
for (int i = 0; i < 6; i++) {
int one_score;
cout << i + 1 << ") "; cin >> one_score;
average_balls.push_back(one_score);
}
employee->set_name(name);
employee->set_academic_performance(average_balls);
employee->discover_avarage_AP();
cout << endl << employee->get_name() << endl;
cout << "Avarage academic performance: " << employee->get_avarage_AP() ;
return 0;
}
Конструктор и деструктор класса
Конструктор - это метод, который вызывается во время создания класса. Также он имеет несколько условий для существования:
- Он должен называться также, как класс.
- У него не должно быть типа функции (bool, void …).
class Worker {
public:
// Конструктор для класса Worker
Worker (string my_name_is, string my_last_name_is)
{
name = string my_name_is;
last_name = my_last_name_is;
}
private:
string name;
string last_name;
};
int main()
{
Worker employee ("Ваня", "Шарапов");
// вот как будет выглядеть, если мы создаем через указатель
Worker *employee_CocaCola = new Worker("Дмитрий","Талтонов");
return 0;
}
Деструктор - тоже функция, только уже вызывается после удаления класса. Кроме условий, которые имеет конструктор, деструктор еще должен начинаться с тильды (~).
class Workers {
public:
// Деструктор класса Workers
~Workers()
{
std::cout << "I'm sorry, my creator(" << std::endl;
}
};
int main()
{
Workers *employee = new Workers;
Worker employee_CocaCola;
// Удаление объекта
delete student; // после этого сработает деструктор employee
return 0;
}
// А вот где сработает деструктор employee_CocaCola
Если хотите всегда быть в курсе последних новостей в мире программирования и IT, подписываетесь на мой Telegram-канал, где я делюсь свежими статьями, новостями и полезными советами. Буду рад видеть вас среди подписчиков!
Обсуждение