Отделение данных от логики
Вынесем реализацию всех методов класса в отдельный файл students.cpp.
#include <string>
#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()
{
return Students::average_ball;
}
А в заголовочном файле students.h оставим только прототипы этих методов.
#pragma once /* Защита от двойного подключения заголовочного файла */
#include <string>
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 <iostream>
#include "students.h"
int main()
{
Students *student = new Students;
std::string name;
std::string last_name;
std::cout << "Name: ";
getline(std::cin, name);
std::cout << "Last name: ";
getline(std::cin, last_name);
student->set_name(name);
student->set_last_name(last_name);
int scores[5];
int sum = 0;
for (int i = 0; i < 5; ++i) {
std::cout << "Score " << i+1 << ": ";
std::cin >> scores[i];
sum += scores[i];
}
student->set_scores(scores);
float average_ball = sum / 5.0;
student->set_average_ball(average_ball);
std::cout << "Average ball for " << student->get_name() << " "
<< student->get_last_name() << " is "
<< student->get_average_ball() << std::endl;
delete student;
return 0;
}
При создании статического объекта, для доступа к его методам и свойствам, используют операция прямого обращения — «. » (символ точки). Если же память для объекта выделяется посредством указателя, то для доступа к его методам и свойствам используется оператор косвенного обращения — «-> ».
Конструктор и деструктор класса
Конструктор класса — это специальная функция, которая автоматически вызывается сразу после создания объекта этого класса. Он не имеет типа возвращаемого значения и должен называться также, как класс, в котором он находится. По умолчанию, заполним двойками массив с промежуточными оценками студента.
class Students {
public:
Students(int default_score)
{
for (int i = 0; i < 5; ++i) {
scores[i] = default_score;
}
}
private:
int scores[5];
};
int main()
{
Students *student = new Students(2);
return 0;
}
Мы можем исправить двойки, если ученик будет хорошо себя вести, и вовремя сдавать домашние задания. А на «нет» и суда нет :-)
Деструктор класса вызывается при уничтожении объекта. Имя деструктора аналогично имени конструктора, только в начале ставится знак тильды ~ . Деструктор не имеет входных параметров.
#include <iostream>
class Students {
public:
~Students()
{
std::cout << "Memory has been cleaned. Good bye." << std::endl;
}
};
int main()
{
Students *student = new Students;
delete student;
return 0;
}
|