Avatar ·

Ошибка "request for member '...' in '...' which is of non-class type" в C++

Всем привет! Я только начал изучать C++ и столкнулся с ошибкой, которую не могу понять. Пишу простую программу для работы с классом "Студент". Вот код: ```cpp #include #include using namespace std; class Student { public: string name; int age; void printInfo() { cout << "Name: " << name << ", Age: " << age << endl; } }; int main() { Student* ptr; ptr = new Student(); // Пытаюсь заполнить данные ptr.name = "Ivan"; ptr.age = 20; ptr.printInfo(); delete ptr; return 0; } ``` Компилятор выдаёт ошибку: "error: request for member 'name' in 'ptr', which is of non-class type 'Student*'". Я понимаю, что ptr — это указатель, но не понимаю, как правильно обращаться к полям объекта через указатель. Почему возникает эта ошибка и как её исправить? Объясните, пожалуйста, на простом примере. <<>>

Причина ошибки

Ошибка возникает из-за того, что ptr — это указатель на объект Student, а не сам объект. Когда вы пишете ptr.name, компилятор пытается найти поле name в типе Student* (указателе), а не в Student. Указатель — это не класс, у него нет членов, поэтому компилятор сообщает: "запрос члена у типа, не являющегося классом".

Решение: используйте оператор разыменования или стрелку

В C++ для доступа к членам объекта через указатель есть два способа:

1. Оператор разыменования * и точка .

Сначала разыменуйте указатель (получите сам объект), затем используйте точку:

(*ptr).name = "Ivan";

Скобки обязательны, потому что оператор точки имеет более высокий приоритет, чем оператор разыменования.

2. Оператор стрелки -> (рекомендуемый способ)

Это синтаксический сахар для предыдущего варианта. Стрелка делает то же самое — разыменовывает указатель и обращается к члену:

ptr->name = "Ivan";

Исправленный код

#include <iostream>
#include <string>
using namespace std;

class Student {
public:
    string name;
    int age;
    
    void printInfo() {
        cout << "Name: " << name << ", Age: " << age << endl;
    }
};

int main() {
    Student* ptr = new Student();
    
    // Правильный способ через стрелку
    ptr->name = "Ivan";
    ptr->age = 20;
    
    // Или через разыменование: (*ptr).name = "Ivan";
    
    ptr->printInfo();  // тоже через стрелку
    
    delete ptr;
    return 0;
}

Важное замечание: когда точка, а когда стрелка

  • Точка (.) — используется, когда у вас есть сам объект (не указатель): Student s; s.name = "Ivan";
  • Стрелка (->) — используется, когда у вас есть указатель на объект: Student* p = new Student(); p->name = "Ivan";

Дополнительный пример для закрепления

Student obj;           // объект на стеке
obj.name = "Petr";     // точка

Student* pObj = &obj;  // указатель на объект
pObj->name = "Petr";   // стрелка

// То же самое через разыменование:
(*pObj).name = "Petr"; // разыменование + точка

Запомните: если переменная объявлена как Тип* — используйте ->. Если как Тип — используйте .. Это универсальное правило для C++.

<<>>

Альтернативный взгляд: работа с объектами на стеке

Если вы только начинаете изучать C++, часто проще избегать ручного управления памятью через new и delete. Вместо указателя можно создать объект непосредственно на стеке:

int main() {
    Student student;  // объект на стеке, не указатель
    
    student.name = "Ivan";
    student.age = 20;
    student.printInfo();
    
    // delete не нужен — объект уничтожится автоматически
    return 0;
}

В этом случае вы используете точку (.) для доступа к членам, и ошибка не возникает. Такой подход проще для понимания и безопаснее — вы не рискуете забыть вызвать delete или получить утечку памяти.

Указатели с new стоит использовать, когда нужно динамическое выделение (например, для полиморфизма, массивов переменного размера или объектов, живущих дольше области видимости). Для учебных примеров на начальном этапе лучше обходиться стековыми объектами.