- •Классы, Объекты и Методы
- •Struct DateStruct {
- •Int month;
- •Int year;
- •Методы классов
- •Void print () {
- •Int main () {
- •Int main () {
- •Int main () {
- •Спецификаторы доступа public и private
- •Int year; // открыто по умолчанию
- •Int main () {
- •Int main () {
- •Int main ()
- •Структуры. Классы
- •Инкапсуляция, Геттеры и Сеттеры Инкапсуляция
- •Int m_array[10];
- •Int main () {
- •IntArray array;
- •Void setValue(int index, int value) {
- •Функции доступа (геттеры и сеттеры)
- •Int m_month;
- •Int m_year;
- •Int main () {
- •Краткий обзор l-value и r-value
- •Инициализация ссылок
- •Ссылки в качестве параметров в функциях
- •Void changeN (int &ref) {
- •Int main () {
- •Int value1;
- •Int otherValue;
- •Ссылки. Указатели
- •Конструкторы
- •Int main () {
- •Конструкторы по умолчанию
- •Конструкторы с параметрами
- •Int m_numerator;
- •Int m_denominator;
- •Int getNumerator () {return m_numerator; }
- •Int getDenominator () {return m_denominator; }
- •Копирующая инициализация
- •Уменьшение количества конструкторов
- •Неявно генерируемый конструктор по умолчанию
- •Int main () {
- •Int main () {
- •Классы, содержащие другие классы
- •Int main() {
- •Список инициализации членов класса
- •Списки инициализации членов класса
- •Int m_value1;
- •Values() {
- •Int m_value1;
- •Инициализация массивов в классе
- •Инициализация переменных-членов, которые являются классами
- •Int main() {
- •Использование списков инициализации
- •Int m_value1;
- •Int m_value1;
- •Int m_value1;
- •Порядок выполнения в списке инициализации
- •Инициализация нестатических членов класса
- •Void print() {
- •Int main() {
- •Void print() {
- •Int main() {
- •Void print() {
- •Int main() {
- •Void print() {
- •Int main() {
- •Делегирующие конструкторы
- •Void DoX() {
- •Init();
- •Init();
- •Void Init()
- •Int main() {
- •Ещё о делегирующих конструкторах
- •Деструкторы
- •Имена деструкторов
- •Пример использования деструктора на практике
- •Выполнение конструкторов и деструкторов
- •Int getId() { return m_nId; }
- •Int main() {
- •Предупреждение о функции exit()
- •Скрытый указатель this
- •Скрытый указатель *this
- •Int m_number;
- •Int getNumber() { return m_number; }
- •Int main() {
- •Указатель this всегда указывает на текущий объект. Явное указание указателя this
- •Int data;
- •Цепочки методов класса
- •Заключение
Int main () {
IntArray array;
array.m_array[16] = 2; // некорректный индекс, вследствие чего
// перезаписываем память, которой мы не владеем
Однако, если мы сделаем массив закрытым, то сможем заставить пользователя использовать функцию, которая первым делом проверяет корректность индекса:
class IntArray {
private:
int m_array[10]; // пользователь не имеет прямого доступа к этому
// члену
public:
Void setValue(int index, int value) {
// Если индекс недействителен, то не делаем ничего
if (index < 0 || index >= 10)
return;
m_array[index] = value;
}
};
Таким образом, мы защитим целостность нашей программы.
Примечание: Функция at() в std::array и std::vector делает что-то похожее!
Преимущество №3: Инкапсулированные классы легче изменить.
Рассмотрим следующий простой пример:
#include <iostream>
class Values {
public:
int m_number1;
int m_number2;
int m_number3;
};
int main () {
Values value;
value.m_number1 = 7;
std::cout << value.m_number1 << '\n';
}
Хотя эта программа работает нормально, но что произойдет, если мы решим переименовать m_number1 или изменить тип этой переменной?
Мы бы сломали не только эту программу, но и большую часть программ, которые используют этот класс Values!
Инкапсуляция предоставляет возможность изменения способа реализации классов, не нарушая при этом работу всех программ, которые их используют.
Вот инкапсулированная версия класса, приведенного выше, но которая использует методы для доступа к m_number1:
#include <iostream>
class Values {
private:
int m_number1;
int m_number2;
int m_number3;
public:
void setNumber1(int number) {m_number1 = number;}
int getNumber1() {return m_number1;}
};
int main () {
Values value;
value.setNumber1(7);
std::cout << value.getNumber1() << '\n';
}
Теперь давайте изменим реализацию класса:
#include <iostream>
class Values {
private:
int m_number [3]; // здесь изменяем реализацию этого класса
public:
// Нам нужно обновить переменные методов, чтобы заработала //новая реализация
void setNumber1(int number) {m_number [0] = number;}
int getNumber1() {return m_number [0];}
};
int main () {
// Но наша программа продолжает работать как прежде
Values value;
value.setNumber1(7);
std::cout << value.getNumber1() << '\n';
}
Обратите внимание, поскольку мы не изменяли прототипы
каких-либо функций в открытом интерфейсе нашего класса, наша программа, использующая класс, продолжает работать без каких-либо изменений или проблем.
Преимущество №4: С инкапсулированными классами легче проводить отладку.
И, наконец, инкапсуляция помогает проводить отладку программ, когда что-то идет не по плану.
Часто причиной неправильной работы программы является некорректное значение одной из переменных.
Если каждый объект имеет прямой доступ к переменной, то отследить часть кода, которая изменила переменную, может быть довольно-таки трудно.
Однако, если для изменения значения нужно вызывать один и тот же метод, вы можете просто использовать точку останова для этого метода и посмотреть, как каждый вызывающий объект изменяет значение, пока не увидите что-то странное.
