Классы в C++: короткая инструкция для начинающих
За полчаса вы создадите первый класс на C++, разложите код по файлам .h и .cpp и скомпилируете рабочую программу. С чего начать и на чём спотыкаются новички.

К концу инструкции у вас будет рабочая программа: класс BankAccount с приватным балансом, методами пополнения и снятия, конструктором и деструктором. Разложите его по заголовочному файлу и файлу реализации, соберёте через g++ и запустите. На это уйдёт 20–40 минут, если C++ вы видите почти впервые.
Что нужно иметь до начала
Проверьте, что всё готово, прежде чем писать первую строку. Иначе застрянете не на классах, а на компиляторе.
- Компилятор с поддержкой C++17: g++ 9+ или clang 10+. Проверьте командой
g++ --version. - Текстовый редактор или IDE — подойдёт VS Code, CLion или любой редактор с подсветкой C++.
- Терминал, где вы умеете переходить в нужную папку (
cd) и запускать команды.
Весь код ниже написан под стандарт C++17. Он же соберётся и в C++20, но флаги в примерах указаны под 17.
Шаг 1. Что такое класс в двух предложениях
Класс — это описание типа: какие данные внутри (поля) и что с ними можно делать (методы). Объект — конкретный экземпляр этого типа в памяти. Одна теоретическая фраза на весь раздел: класс отвечает за свои данные сам, поэтому поля прячут внутрь, а наружу отдают методы.
Проверка: если понятно, что класс — это чертёж, а объект — деталь по чертежу, идём дальше.
Шаг 2. Первый класс в одном файле
Начнём с самого компактного варианта — всё в одном файле main.cpp. Создайте его и вставьте целиком:
#include <iostream>
#include <string>
// Класс банковского счёта: хранит баланс и имя владельца
class BankAccount {
private:
std::string owner; // имя владельца, недоступно снаружи
double balance; // текущий баланс, недоступно снаружи
public:
// Конструктор: вызывается при создании объекта
BankAccount(const std::string& name, double start) {
owner = name;
balance = start;
std::cout << "Счёт открыт: " << owner << "\n";
}
// Деструктор: вызывается при уничтожении объекта
~BankAccount() {
std::cout << "Счёт закрыт: " << owner << "\n";
}
// Пополнение счёта
void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
// Снятие с проверкой остатка
bool withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false;
}
// Чтение баланса. const означает, что метод не меняет объект
double getBalance() const {
return balance;
}
};
int main() {
BankAccount acc("Иван", 1000.0);
acc.deposit(500.0);
if (acc.withdraw(2000.0)) {
std::cout << "Снято 2000\n";
} else {
std::cout << "Недостаточно средств\n";
}
std::cout << "Баланс: " << acc.getBalance() << "\n";
return 0;
}
Соберите и запустите:
g++ -std=c++17 main.cpp -o account
./account
Проверка: в терминале появятся строки «Счёт открыт: Иван», «Недостаточно средств», «Баланс: 1500» и в конце «Счёт закрыт: Иван». Последняя строка — это сработавший деструктор, когда acc вышел из области видимости в конце main.
Почему поля под private
Уберите слово private и попробуйте в main написать acc.balance = 999999; — соберётся. Верните private — компилятор откажется: 'balance' is a private member. В этом весь смысл: снаружи баланс меняют только через deposit и withdraw, где есть проверки.
Приватные поля и публичные методы — не формальность. Это единственная точка, где вы контролируете, что происходит с данными. Открыли поле наружу — потеряли контроль.
Шаг 3. Разложить класс по двум файлам
В реальных проектах объявление класса кладут в заголовочный файл (.h), а реализацию методов — в .cpp. Так код компилируется быстрее и не дублируется.
Файл BankAccount.h
#ifndef BANKACCOUNT_H
#define BANKACCOUNT_H
#include <string>
class BankAccount {
private:
std::string owner;
double balance;
public:
BankAccount(const std::string& name, double start);
~BankAccount();
void deposit(double amount);
bool withdraw(double amount);
double getBalance() const;
};
#endif // BANKACCOUNT_H
Строки #ifndef / #define / #endif — это защита от повторного включения. Без неё при подключении заголовка из нескольких файлов класс объявится дважды и компилятор выдаст ошибку.
Файл BankAccount.cpp
#include "BankAccount.h"
#include <iostream>
// Перед именем метода — имя класса и два двоеточия
BankAccount::BankAccount(const std::string& name, double start) {
owner = name;
balance = start;
std::cout << "Счёт открыт: " << owner << "\n";
}
BankAccount::~BankAccount() {
std::cout << "Счёт закрыт: " << owner << "\n";
}
void BankAccount::deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
bool BankAccount::withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false;
}
double BankAccount::getBalance() const {
return balance;
}
Файл main.cpp
#include "BankAccount.h"
#include <iostream>
int main() {
BankAccount acc("Иван", 1000.0);
acc.deposit(500.0);
acc.withdraw(300.0);
std::cout << "Баланс: " << acc.getBalance() << "\n";
return 0;
}
Теперь в сборке участвуют оба .cpp, а .h подключать в команде не нужно:
g++ -std=c++17 main.cpp BankAccount.cpp -o account
./account
Проверка: программа выведет «Баланс: 1200» и закроет счёт. Если вы забудете добавить BankAccount.cpp в команду, получите ошибку компоновщика — о ней ниже.
Синтаксис, который путают чаще всего
Три места, где легко ошибиться, когда только начинаешь:
| Элемент | Как правильно | Частая ошибка |
|---|---|---|
| Точка с запятой после класса | }; в конце объявления | Забыть — компилятор выдаст непонятную ошибку в следующей строке |
| Реализация метода в .cpp | void BankAccount::deposit(...) | Написать void deposit(...) без имени класса |
| Метод, не меняющий объект | double getBalance() const | Забыть const у геттеров |
Если не получилось
undefined reference to 'BankAccount::BankAccount(...)'— ошибка компоновщика. Вы не добавилиBankAccount.cppв команду сборки. Соберите все.cppвместе, как в шаге 3.'balance' is a private member of 'BankAccount'— вы обращаетесь к приватному полю снаружи класса. Используйте публичный метод или перенесите код внутрь класса.expected ';' after class definition— забыли точку с запятой после закрывающей фигурной скобки класса. Проверьте строку с}после последнего метода.redefinition of class 'BankAccount'— заголовок подключился дважды. Проверьте, что в.hесть#ifndef / #define / #endifили строка#pragma onceв самом верху.fatal error: BankAccount.h: No such file or directory— файл лежит не в той папке или имя написано с другим регистром. Держите все три файла в одной директории.
Что делать дальше
Класс работает — дальше стоит разобраться с тремя вещами именно в этом порядке. Сначала список инициализации конструктора (: owner(name), balance(start) вместо присваиваний в теле) — это правильный способ задавать поля. Затем — что происходит при копировании объекта: конструктор копирования и правило пяти. И только потом наследование и виртуальные методы. Управление памятью через new/delete в классе — отдельная большая тема; в современном C++ её закрывают std::unique_ptr и std::vector, к ним и идите.
Репетитор 1—11 класс и ВУЗ: Математика и физика методом наведения на мысль, помощь с ДЗ.
Спросить за 8 ₽Источники: cppreference — Classes, ISO C++ — Standard C++ FAQ
Частые вопросы
А если у меня компилятор старее, чем g++ 9?
Обязательно ли разбивать класс на .h и .cpp?
Чем private отличается от public и protected?
Нужно ли всегда писать деструктор?
Собралось, но при запуске программа падает или выводит мусор в балансе. Что не так?
Зачем const после getBalance() и можно ли без него?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.