Практический курс по C/C++: https://stepik.org/course/193691
Продолжаем тему
структур языка С++. На прошлом занятии мы с вами увидели, что в структурах
можно объявлять методы и вызывать их через объект-структуру (переменные
структуры):
#include <iostream>
#include <math.h>
using std::cout;
using std::endl;
struct point {
int x, y;
double length() { return sqrt(x*x + y*y); }
};
int main()
{
struct point pt {1, 2};
cout << pt.length() << endl;
return 0;
}
Причем, значения
полей x и y мы можем
задавать, как при инициализации, так и через переменную pt, ровно так, как
это было и в языке Си:
Однако это не
всегда желаемое поведение. Например, у нас может быть ограничение, что точка
типа point принимает значения
координат из строго заданного диапазона. Для примера возьмем его [-100; 100].
Тогда непосредственное изменение состояния объекта pt через переменные
x и y легко может
привести к нарушению этого требования. Например:
pt.x = 153; // за пределами [-100; 100]
Как же быть? Для
этого в структурах и классах языка С++ дополнительно можно определять режимы
доступа к тем или иным полям структуры (класса). На данный момент мы рассмотрим
два таких режима:
- public – публичный
доступ к переменным и методам;
- private – частный
(закрытый) доступ к переменным и методам.
Например, если
нам нужно закрыть прямой доступ извне для каких-либо полей структуры, то
достаточно объявить их как private следующим образом:
struct point {
private:
int x, y;
public:
double length() { return sqrt(x*x + y*y); }
};
То есть, пишется
ключевое слово private и ставится двоеточие. Все, что следует
ниже, попадает в раздел private до тех пор, пока не встретится
какой-либо другой режим доступа. Так как мы хотим, чтобы метод length() был
общедоступным, то необходимо явно прописать режим public после private.
Давайте
посмотрим, к чему это приведет. Теперь мы не можем выполнять инициализацию
объекта pt, так как
инициализируемые поля x и y скрыты от
внешнего доступа. Также мы не можем изменять значения этих полей, обращаясь к
ним напрямую через объект pt:
А вот вызывать
метод length() по-прежнему
можно:
double res = pt.length(); // ok
Причем, внутри
метода length() обращение к
приватным переменным x и y разрешено. То
есть, режим доступа private запрещает работать с переменными x и y напрямую извне
структуры point, но не
запрещает делать это изнутри. А режим public позволяет
прямое обращение как извне структуры, так и внутри нее. По умолчанию все поля
структуры помечаются как public. Именно поэтому на прошлом
занятии мы даже не подозревали о существовании каких-то режимов доступа.
Конечно, защита
на уровне private отдельных полей
структуры – это защита для программиста от своих собственных случайных ошибок. Если
что-либо помечено как private, значит, напрямую к этим
переменным или методам обращаться не следует. Хотя, конечно, обойти эту защиту
достаточно просто. Поэтому мы здесь закрывает поля не от злоумышленников, а для
корректного написания кода, уменьшая собственные возможные ошибки.
И еще одно
важное замечание. Защита private определяется на
уровне типа данных – структуры целиком, а не на уровне отдельных объектов. О
чем здесь речь? Смотрите, если в нашей структуре point объявить еще
один метод, например, sum() для сложения одного вектора с другим:
struct point {
private:
int x, y;
public:
double length() { return sqrt(x*x + y*y); }
void sum(const point& pt)
{
this->x += pt.x;
this->y += pt.y;
}
};
То внутри метода
sum мы можем совершенно
спокойно обращаться к переменным x и y через
переданный объект pt. И все благодаря тому, что функция-член sum() принадлежит
типу данных point. Она находится,
как бы, внутри него. А раз, так, то автоматически получает доступ ко всем
приватным полям этой структуры, даже если они берутся из другого объекта. Поэтому
и говорят, что защита действует на уровне типа данных, а не на уровне объектов.
Конечно,
полученная структура point, на данный момент не пригодна для
практического использования. Мы не можем задавать нужные нам координаты и
читать их. Самый очевидный шаг, как это можно было бы поправить – это
определить соответствующие публичные методы. Например, следующим образом:
struct point {
private:
int x, y;
public:
double length() { return sqrt(x*x + y*y); }
void sum(const point& pt)
{
this->x += pt.x;
this->y += pt.y;
}
void set_coords(int x, int y)
{
if(x < -100 || x > 100 || y < -100 || y > 100)
return;
this->x = x;
this->y = y;
}
void get_coords(int& x, int& y) {x = this->x; y = this->y; }
int get_x() { return this->x; }
int get_y() { return this->y; }
};
Обратите
внимание, что в методах set_coords() и get_coords() для обращения
к переменным x и y текущего
объекта необходимо использовать неявный указатель this. Если бы мы
прописали просто x, то это соответствовало бы локальному параметру x функции, а не
переменной объекта. Поэтому в таких случаях, когда локальные переменные внутри
метода совпадают по именам с полями структуры, для обращения к полям необходимо
использовать указатель this на текущий объект.
Теперь мы можем передавать
координаты и читать их, используя публичные методы:
int main()
{
struct point pt;
pt.set_coords(1, 2);
cout << pt.get_x() << " " << pt.get_y() << endl;
double res = pt.length();
cout << res << endl;
return 0;
}
В ООП методы,
которые служат для установки значений переменных объекта, получили название сеттеры
(от префикса set, который часто
записывается вначале таких методов), а методы, с помощью которых читаются
значения переменных объекта – геттерами (от префикса get).
Однако и в такой
реализации структура point все еще остается
недоработанной. Это связано с тем, что когда мы создаем объект pt, то его
начальное состояние оказывается неопределенным из-за неизвестных значений
координат x и y. Но следующие
улучшения мы будем делать уже на следующем занятии.
Практический курс по C/C++: https://stepik.org/course/193691