Структуры в С++, как обновленный тип данных

Практический курс по C/C++: https://stepik.org/course/193691

Начиная с этого занятия мы с вами сделаем первый маленький шажок в мир ООП языка С++ и начнем со структур. Да, да, структуры в языке С++ преобразились и стали, фактически, классами. Но обо всем по порядку.

Понятие структур нам уже известно по языку Си. В С++ они формально объявляются похожим образом. Например, так:

struct point {
    int x, y;
};

То есть, пишется ключевое слово struct, затем, имя типа структуры – point, в фигурных скобках прописываются поля (переменные) структуры и в конце ставится точка с запятой.

Первое важное отличие такой структуры в С++ от аналогичной в языке Си, заключается в том, что имя point здесь является полноценным типом, а не просто тегом структуры, как это было в Си. То есть, далее по программе мы можем объявить переменную типа point следующим образом:

int main()
{
    point pt;
    return 0;
}

Тогда как в Си нам необходимо было прописывать:

struct point pt;

Кстати, в языке С++ допустимы обе формы записи типа структуры. Сделано это было для обратной совместимости с языком Си. Однако, если мы пишем программу исключительно для компилятора С++, то слово 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

Видео по теме