Управление доступом. Инкапсуляция
В прошлой теме использовался следующий класс Person:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person
{
public:
string name;
int age;
Person(string n, int a)
{
name = n; age = a;
}
void move()
{
cout << userName << " is moving" << endl;
}
};
Спецификатор public является спецификатором доступа (access specifier), то есть определяет параметры доступа к членам класса - переменным и функциям. В частности, он делает их доступными из любой части программы или открытыми. По сути спецификатор public определяет общедоступный интерфейс класса.
То есть в данном случае поля name и age являются открытыми, и мы можем присвоить им во внешнем коде любые значения:
1
2
3
Person tom("Tom", 22);
tom.name = "abyrvalk";
tom.age = -1001;
В том числе можно присвоить какие-то недопустимые значения, например, отрицательное значение для возраста пользователя. Естественно это не очень хорошая ситуация.
Однако с помощью другого спецификатора private мы можем скрыть реализацию членов класса, то есть сделать их закрытыми, инкапсулировать внутри класса.
Перепишем класс Person с исключением спецификатора private:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <string>
using std::string;
using std::cout;
using std::endl;
class Person
{
public:
Person(string n, int a)
{
name = n; age = a;
}
void move()
{
cout << name << " is moving" << endl;
}
void setAge(int a)
{
if (a > 0 && a < 100) age = a;
}
string getName()
{
return name;
}
int getAge()
{
return age;
}
private:
string name;
int age;
};
int main()
{
Person tom("Tom", 22);
// string personName = tom.name; // ошибка - переменная name закрытая
cout << "Name: " << tom.getName() << "\tAge: " << tom.getAge() << endl;
tom.setAge(31);
cout << "Name: " << tom.getName() << "\tAge: " << tom.getAge() << endl;
tom.setAge(291);
cout << "Name: " << tom.getName() << "\tAge: " << tom.getAge() << endl;
return 0;
}
Теперь переменные name и age в классе Person являются закрытыми, поэтому мы не можем обратиться к ним напрямую. Мы можем к ним обращаться только внутри класса.
Чтобы все таки можно было получить извне значения переменных name и age, определены дополнительные функции getAge и getName. Установить значение переменной name напрямую можно только через конструктор, а значение переменной age - через конструктор или через функцию setAge. При этом функция setAge устанавливает значение для переменной age, если оно соответствует определенным условиям.
Таким образом, состояние класса скрыто извне, к нему можно получить доступ только посредством дополнительно определенных функций, который представляют интерфейс класса.
Также стоит отметить, что если в классе отсутствует спецификатор доступа или для некоторых членов класса он не определен, то по умолчанию используется спецификатор доступа private:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Person
{
string name;
int age;
public:
Person(string n, int a)
{
name = n; age = a;
}
void move()
{
cout << name << " is moving" << endl;
}
void setAge(int a)
{
if (a > 0 && a < 100) age = a;
}
string getName()
{
return name;
}
int getAge()
{
return age;
}
};
Для переменных name и age здесь не определен спецификатор доступа, поэтому для них по умолчанию используется спецификатор private.