Практический курс по C/C++: https://stepik.org/course/193691
Начиная с этого
занятия мы с вами сделаем первый маленький шажок в мир ООП языка С++ и начнем
со структур. Да, да, структуры в языке С++ преобразились и стали, фактически,
классами. Но обо всем по порядку.
Понятие структур
нам уже известно по языку Си. В С++ они формально объявляются похожим образом.
Например, так:
struct point {
int x, y;
};
То есть, пишется
ключевое слово struct, затем, имя типа структуры – point, в фигурных
скобках прописываются поля (переменные) структуры и в конце ставится точка с запятой.
Первое важное
отличие такой структуры в С++ от аналогичной в языке Си, заключается в том, что
имя point здесь является
полноценным типом, а не просто тегом структуры, как это было в Си. То есть,
далее по программе мы можем объявить переменную типа point следующим
образом:
int main()
{
point pt;
return 0;
}
Тогда как в Си
нам необходимо было прописывать:
Кстати, в языке
С++ допустимы обе формы записи типа структуры. Сделано это было для обратной
совместимости с языком Си. Однако, если мы пишем программу исключительно для
компилятора С++, то слово struct при объявлении переменных
опускают.
Далее, мы можем
инициализировать поля этой структуры следующим образом:
struct point pt {}; // инициализация нулями
struct point pt {1}; // инициализация x=1, y=0
struct point pt {1, 2}; // инициализация x=1, y=2
Функции-члены (методы)
Следующее
ключевое отличие структур языка С++ - это возможность объявление функций
непосредственно внутри структуры. Например, так:
struct point {
int x, y;
double length() { return sqrt(x*x + y*y); }
};
Функция length() вычисляет
длину радиус-вектора и возвращает вычисленное значение. Такие функции получили
название функции-члены или, чаще всего говорят, методы.
Особенностью
методов является прямой доступ к переменным объекта, для которого этот метод
был вызван. Например, следующий фрагмент программы вычисляет с помощью метода length() длины двух
разных объектов point:
int main()
{
struct point pt {1, 2};
struct point pt_2 {3, 4};
cout << pt.length() << endl;
cout << pt_2.length() << endl;
return 0;
}
Давайте
детальнее разберемся, как это работает. При объявлении метода length() мы просто
записали имена переменных x и y, объявленных
внутри структуры. Но у каждого объекта pt и pt_2 свои
локальные переменные x и y со своими
значениями. Так откуда же метод length() «знает» какие переменные
использовать и как получает к ним доступ? В действительности, каждому методу
автоматически и неявно передается специальный указатель с именем this. Его так и
называют – неявный указатель на объект. А раз так, то мы, по идее, можем
обратиться из метода к локальным переменным текущего объекта, следующим
образом:
struct point {
int x, y;
double length() { return sqrt(this->x*this->x + this->y*this->y); }
};
И,
действительно, это будет эквивалентом предыдущей записи. На самом деле, когда
явно не прописывается параметр this, то он подразумевается при обращении к
полям структуры. Именно так метод length() получает
доступ к нужным переменным для вычисления длины радиус-вектора. Как видите, все
просто.
Конечно, неявный
указатель this имеет тот же
тип, что и текущая структура. В нашем примере – это тип point*. Он доступен
практически в любом методе структуры, за исключением статических методов.
Например, если мы попробуем записать такой метод и обратиться через указатель this к полю
структуры, то получим ошибку:
struct point {
int x, y;
double length() { return sqrt(this->x*this->x + this->y*this->y); }
static void show_coords() {cout << this->x; } // ошибка, this не существует
};
И это логично,
так как статические методы не связаны с конкретным объектом, а просто являются
функцией внутри области видимости структуры.
Вообще через
указатель this мы можем
обращаться к любым полям структуры, даже если это другие методы. Например, так:
struct point {
int x, y;
double length() { return sqrt(this->get_x()*this->get_x() + this->get_y()*this->get_y()); }
int get_x() { return x; }
int get_y() { return y; }
};
Или, без
ключевого слова this:
struct point {
int x, y;
double length() { return sqrt(get_x()*get_x() + get_y()*get_y()); }
int get_x() { return x; }
int get_y() { return y; }
};
Но тогда оно
подразумевается.
Конечно, это
несколько искусственный пример. В данном случае было бы правильнее обращаться
напрямую к переменным x и y. Это лишь
демонстрация возможности указателя this.
На следующем
занятии мы продолжим эту тему и увидим, какие еще возможности появились у
структур языка С++.
Практический курс по C/C++: https://stepik.org/course/193691