Класс в C++ — это пользовательский тип данных, который объединяет данные (поля) и работающие с ними функции (методы) в одну сущность. Объект — это конкретная переменная такого типа. Аналогия-якорь: класс — это чертеж, объект — построенный по нему дом; чертеж один, домов по нему может быть много.
Содержание
Ниже разберем именно C++ (не Java и не C#): как объявить class, задать поля и методы, закрыть данные модификаторами доступа, написать конструктор и деструктор, для чего нужен указатель this, чем class отличается от struct и как выглядит простое наследование. Базовый синтаксис классов стабилен, поэтому примеры одинаково работают в C++17, C++20 и C++23.
Минимальный рабочий пример
Начнем с законченного кода, который компилируется и запускается. Класс Counter хранит одно число и умеет его увеличивать.
#include <iostream>
class Counter {
private:
int value; // поле, закрытое снаружи
public:
Counter(int start) { // конструктор
value = start;
}
void increment() { // метод: меняет состояние
value++;
}
int get() const { // метод чтения: объект не меняет
return value;
}
};
int main() {
Counter c(10); // конструктор ставит value = 10
c.increment();
c.increment();
std::cout << c.get() << "\n";
return 0;
}
В консоли появится 12: старт 10, два вызова increment() дают 12, get() возвращает результат. Разберем этот код по частям.
Объявление class, поля и методы
Объявление начинается со слова class, за ним идет имя типа и тело в фигурных скобках. После закрывающей скобки обязательна точка с запятой — частая ошибка новичка ее пропустить.
Внутри тела различают две вещи. Поля (данные-члены) — это переменные, которые хранит каждый объект; у Counter это value. Методы (функции-члены) — это функции внутри класса, работающие с его полями; у Counter это increment и get.
Метод, который не изменяет объект, помечают словом const после списка параметров: int get() const. Компилятор тогда запретит случайную запись в поля внутри такого метода — это защита от ошибок, а не просто пометка.
Обращение к членам объекта идет через точку (c.increment()), а через указатель на объект — стрелкой (p->increment()).
Модификаторы доступа: public, private, protected
Модификатор доступа задает, откуда виден член класса. Их три, и они действуют на все члены до следующего модификатора.
| Модификатор | Доступ изнутри класса | Доступ у наследника | Доступ извне (через объект) |
|---|---|---|---|
private |
да | нет | нет |
protected |
да | да | нет |
public |
да | да | да |
private — данные видны только методам самого класса. protected — как private, но еще открыт классам-наследникам. public — открытый интерфейс, доступный из любого места программы.
Попытка обратиться к закрытому полю снаружи не пройдет компиляцию. Покажем ошибку тройкой.
Неверно:
Counter c(5);
c.value = 100; // прямая запись в приватное поле
Фактический результат — ошибка на этапе компиляции (точная формулировка зависит от компилятора, у GCC и Clang текст немного разный):
error: 'int Counter::value' is private within this context
Исправление — менять поле только через метод, который контролирует значение:
Counter c(5);
c.increment(); // разрешенный путь: публичный метод
Инкапсуляция: почему поля закрывают
Инкапсуляция — это сокрытие внутренних данных за публичными методами, чтобы объект сам следил за корректностью своего состояния. Общепринятый стиль в C++: поля делают private, а доступ к ним дают через методы (их часто зовут геттеры и сеттеры).
Смысл не в самих геттерах, а в контроле: сеттер может проверить значение и не пустить в объект мусор.
void set(int value) { // метод класса Temperature
if (value < -273) { // ниже абсолютного нуля не бывает
celsius = -273;
} else {
celsius = value;
}
}
Если вызвать t.set(-500), поле celsius станет -273, а не -500 — сеттер отклонил недопустимое значение. При прямом доступе к полю такой защиты бы не было; в этом практическая польза закрытых данных.
Конструкторы и деструкторы
Конструктор — это метод, который вызывается автоматически при создании объекта и приводит его в корректное начальное состояние. Имя конструктора совпадает с именем класса, тип возврата не указывается.
Начальные значения полей лучше задавать через список инициализации — двоеточие после списка параметров: Rectangle(int w, int h) : width(w), height(h) {}. Это эффективнее присваивания в теле и обязательно для полей-констант и ссылок.
Деструктор — это метод, который вызывается автоматически при уничтожении объекта (например, при выходе из области видимости). Его имя — тильда плюс имя класса: ~Rectangle(). Деструктор освобождает то, что объект захватил: память, файл, соединение.
Пример с проговоренным порядком вызовов:
#include <iostream>
#include <string>
class FileGuard {
private:
std::string name;
public:
FileGuard(const std::string& fileName) : name(fileName) {
std::cout << "open " << name << "\n";
}
~FileGuard() {
std::cout << "close " << name << "\n";
}
};
int main() {
FileGuard f("data.txt");
std::cout << "work\n";
return 0;
}
Вывод будет таким:
open data.txt
work
close data.txt
Конструктор отработал при создании f, а деструктор — сам, в конце main, когда объект уходит из области видимости. Это идиома RAII (Resource Acquisition Is Initialization): ресурс захватывают в конструкторе, освобождают в деструкторе, и освобождение происходит автоматически, даже если функция завершится из-за исключения. Граница упрощения: для владения динамической памятью в современном C++ обычно берут не ручной new/delete, а готовые типы (std::string, std::vector, std::unique_ptr), которые реализуют RAII за вас.
Указатель this
this — это указатель внутри метода на тот объект, для которого метод вызван. Он нужен, когда имя параметра совпадает с именем поля: this-> явно указывает на поле, а не на параметр.
class Point {
private:
int x;
int y;
public:
Point(int x, int y) {
this->x = x; // слева - поле, справа - параметр
this->y = y;
}
int getX() const { return x; }
};
Без this-> строка x = x присвоила бы параметр самому себе, а поле осталось бы неинициализированным. Список инициализации : x(x), y(y) решает ту же задачу без this.
class против struct в C++
В C++ class и struct — почти одно и то же, отличие только в доступе по умолчанию. Это важное различие, которое часто путают.
| Свойство | class |
struct |
|---|---|---|
| Доступ по умолчанию | private |
public |
| Наследование по умолчанию | private |
public |
| Может иметь методы, конструкторы, наследование | да | да |
То есть struct в C++ — это не «структура из чистого Си без методов»; у него есть методы, конструкторы и наследование, просто члены по умолчанию открыты. На практике struct берут для простых наборов данных, а class — когда важна инкапсуляция; это соглашение о стиле, а не требование языка.
Простое наследование
Наследование позволяет одному классу (наследнику) получить поля и методы другого (базового) и добавить свои. Запись class Dog : public Animal означает публичное наследование.
#include <iostream>
#include <string>
class Animal {
protected:
std::string name; // protected: открыто наследнику, закрыто извне
public:
Animal(const std::string& n) : name(n) {}
void eat() const {
std::cout << name << " eats\n";
}
};
class Dog : public Animal {
public:
Dog(const std::string& n) : Animal(n) {} // вызов конструктора базы
void bark() const {
std::cout << name << " barks\n"; // доступ к protected-полю базы
}
};
int main() {
Dog d("Rex");
d.eat(); // Rex eats
d.bark(); // Rex barks
return 0;
}
В консоли появится две строки: Rex eats и Rex barks. Наследник Dog получил метод eat от Animal и добавил свой bark; поле name доступно ему, потому что оно protected, а конструктор наследника передает имя в конструктор базы через список инициализации.
Это минимальный случай. Полиморфизм через virtual-методы и абстрактные классы — отдельная большая тема, которую здесь не раскрываем.
Выводы
- Класс в C++ объявляют словом
class, он объединяет поля (данные) и методы (функции); объект — это конкретная переменная этого типа. - Модификаторы
private,protectedиpublicзадают видимость членов; типичный стиль — закрытые поля и публичные методы. - Конструктор задает начальное состояние объекта, деструктор освобождает ресурсы; связка «захват в конструкторе — освобождение в деструкторе» называется RAII.
- Указатель
thisразличает поле и одноименный параметр; список инициализации часто решает ту же задачу чище. - В C++
classиstructразличаются только доступом по умолчанию:privateу class,publicу struct.
Где применяется / связь с практикой
Классы — это фундамент любого нетривиального кода на C++: движки игр, встраиваемые системы, торговые и высоконагруженные сервисы строятся на пользовательских типах с инкапсуляцией и RAII. Умение грамотно закрыть данные, написать корректный конструктор и деструктор и не утечь ресурсами — базовый навык C++-разработчика, который проверяют на собеседованиях.
Освойте тему на практике
Если хотите системно пройти путь от синтаксиса классов до управления памятью и многопоточности, посмотрите курс C++ Basic — там разбор классов идет на практических задачах с проверкой кода. Понять формат преподавания и уровень требований помогают открытые уроки Otus — это бесплатные занятия по темам курсов.
FAQ
Обязательно ли писать конструктор?
Нет. Если не объявить ни одного конструктора, компилятор сгенерирует конструктор по умолчанию. Но как только вы напишете свой конструктор с параметрами, автоматический конструктор без параметров пропадет — его придется объявить явно, если он нужен.
Когда именно вызывается деструктор?
Для локального объекта — при выходе из его области видимости (закрывающая скобка блока). Для объекта, созданного через new, — в момент delete. Порядок уничтожения локальных объектов обратный порядку создания.
Можно ли методам класса обращаться к приватным полям другого объекта того же класса?
Да. Модификатор доступа действует на уровне класса, а не отдельного объекта. Метод класса Counter может читать приватные поля любого переданного ему объекта Counter, например при сравнении двух счетчиков.



