⚙️ C++

Die while- und do-while-Schleife in C++17: Eine ausführliche Anleitung

ООП в C++: классы, наследование и полиморфизм с примерами

C++ — один из первых языков, сделавших ООП популярным. В отличие от Java, в C++ ООП — это выбор, а не обязательство: можно писать и процедурно. Но ООП даёт мощные инструменты для организации сложного кода.

Класс и объект: аналогия с животными

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

class Dog {
public:  // Публичный раздел: доступен всем
    string name;
    string breed;
    int age;
    
    // Методы
    void bark() {
        cout << name << " (" << breed << "): ГАВ-ГАВ!" << endl;
    }
    
    void eat(string food) {
        cout << name << " ест " << food << endl;
    }
    
    void describe() {
        cout << name << ", " << breed << ", " << age << " лет" << endl;
    }
};  // Точка с запятой обязательна!

int main() {
    // Создание объектов (переменных типа Dog)
    Dog rex;
    rex.name  = "Рекс";
    rex.breed = "Немецкая овчарка";
    rex.age   = 3;
    
    Dog sharik;
    sharik.name  = "Шарик";
    sharik.breed = "Дворняжка";
    sharik.age   = 7;
    
    // Каждый объект независим!
    rex.bark();
    sharik.bark();
    rex.describe();
    sharik.describe();
    
    return 0;
}

Конструктор и деструктор

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

class Person {
private:  // Приватный раздел: доступен только внутри класса
    string name;
    int age;
    
public:
    // Конструктор: вызывается при создании объекта
    // Список инициализации — более эффективный способ
    Person(string name, int age) : name(name), age(age) {
        cout << "Создан: " << name << endl;
    }
    
    // Конструктор по умолчанию
    Person() : name("Неизвестно"), age(0) {
        cout << "Создан пустой Person" << endl;
    }
    
    // Конструктор копирования
    Person(const Person& other) : name(other.name + " (копия)"), age(other.age) {
        cout << "Скопирован: " << name << endl;
    }
    
    // Деструктор: вызывается при удалении объекта
    ~Person() {
        cout << "Удалён: " << name << endl;
    }
    
    // Геттеры
    string getName() const { return name; }  // const: метод не изменяет объект
    int    getAge()  const { return age; }
    
    // Сеттер с валидацией
    void setAge(int newAge) {
        if (newAge >= 0 && newAge <= 150) age = newAge;
        else cout << "Ошибка: некорректный возраст" << endl;
    }
    
    void introduce() const {
        cout << "Привет! Я " << name << ", мне " << age << " лет." << endl;
    }
};

int main() {
    Person ivan("Иван", 25);    // Конструктор с параметрами
    Person empty;                // Конструктор по умолчанию
    Person copy = ivan;          // Конструктор копирования
    
    ivan.introduce();
    copy.introduce();
    
    ivan.setAge(26);
    ivan.setAge(200); // Ошибка: некорректный возраст
    
    // Деструктор вызовется автоматически при выходе из области видимости
    cout << "Конец main:" << endl;
    // Здесь вызываются деструкторы в обратном порядке создания
    
    return 0;
}

Инкапсуляция: public, private, protected

#include <iostream>
using namespace std;

class BankAccount {
private:             // Доступно только внутри класса
    string owner;
    double balance;
    string pin;
    int failedAttempts;
    
protected:           // Доступно в классе и наследниках
    bool isBlocked;
    
public:              // Доступно везде
    BankAccount(string owner, double balance, string pin)
        : owner(owner), balance(max(0.0, balance)),
          pin(pin), failedAttempts(0), isBlocked(false) {}
    
    // Геттеры (const — не изменяют объект)
    string getOwner()  const { return owner; }
    double getBalance() const { return balance; }
    bool   getBlocked() const { return isBlocked; }
    
    // Пополнение
    void deposit(double amount) {
        if (isBlocked) { cout << "Счёт заблокирован!" << endl; return; }
        if (amount <= 0) { cout << "Сумма должна быть > 0" << endl; return; }
        balance += amount;
        cout << "Пополнение +" << amount << "₽. Баланс: " << balance << "₽" << endl;
    }
    
    // Снятие с PIN
    bool withdraw(double amount, string enteredPin) {
        if (isBlocked) { cout << "Счёт заблокирован!" << endl; return false; }
        if (enteredPin != pin) {
            if (++failedAttempts >= 3) { isBlocked=true; cout << "ЗАБЛОКИРОВАН!" << endl; }
            else cout << "Неверный PIN. Попытка " << failedAttempts << "/3" << endl;
            return false;
        }
        if (amount > balance) { cout << "Недостаточно средств" << endl; return false; }
        balance -= amount; failedAttempts=0;
        cout << "Снято -" << amount << "₽. Остаток: " << balance << "₽" << endl;
        return true;
    }
};

int main() {
    BankAccount acc("Иван", 50000, "1234");
    acc.deposit(15000);
    acc.withdraw(5000, "9999"); // Неверный PIN
    acc.withdraw(5000, "1234"); // Правильный
    
    // acc.balance = 1e9; // ОШИБКА: balance is private
    
    return 0;
}

Наследование

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

// Базовый класс
class Animal {
protected:
    string name;
    int age;
    
public:
    Animal(string name, int age) : name(name), age(age) {}
    
    void breathe() { cout << name << " дышит" << endl; }
    void eat(string food) { cout << name << " ест " << food << endl; }
    
    virtual string sound() { return "..."; } // virtual — можно переопределить
    
    void makeSound() {
        cout << name << " говорит: " << sound() << endl;
    }
    
    string info() const {
        return name + " (" + to_string(age) + " лет)";
    }
};

// Dog наследует Animal
class Dog : public Animal {
    string breed;
public:
    Dog(string name, int age, string breed) : Animal(name, age), breed(breed) {}
    
    void bark() { cout << name << " (" << breed << "): ГАВ!" << endl; }
    
    string sound() override { return "ГАВ-ГАВ!"; } // override — переопределяем
    
    string info() const {
        return Animal::info() + ", порода: " + breed;
    }
};

// Cat наследует Animal
class Cat : public Animal {
    bool indoor;
public:
    Cat(string name, int age, bool indoor) : Animal(name, age), indoor(indoor) {}
    
    void purr() { cout << name << " мурлычет ♪" << endl; }
    
    string sound() override { return "МЯУ~"; }
    
    string info() const {
        return Animal::info() + string(indoor ? ", домашняя" : ", уличная");
    }
};

int main() {
    Dog rex("Рекс", 3, "Овчарка");
    Cat mur("Мурка", 5, true);
    
    // Унаследованные методы
    rex.breathe(); rex.eat("мясо");
    mur.breathe();
    
    // Специфичные методы
    rex.bark();
    mur.purr();
    
    // Переопределённые
    rex.makeSound(); // Рекс говорит: ГАВ-ГАВ!
    mur.makeSound(); // Мурка говорит: МЯУ~
    
    cout << rex.info() << endl;
    cout << mur.info() << endl;
    
    return 0;
}

Виртуальные функции и полиморфизм

#include <iostream>
#include <cmath>
#include <vector>
#include <memory>  // unique_ptr
using namespace std;

class Shape {
protected:
    string color;
public:
    Shape(string c) : color(c) {}
    
    virtual ~Shape() {} // Виртуальный деструктор — ОБЯЗАТЕЛЕН при наследовании!
    
    virtual double area() const = 0;       // = 0: чисто виртуальная функция
    virtual double perimeter() const = 0;  // Класс становится абстрактным
    
    void describe() const {
        cout << "[" << color << "] площадь=" << area()
             << ", периметр=" << perimeter() << endl;
    }
};

class Circle : public Shape {
    double r;
public:
    Circle(string c, double r) : Shape(c), r(r) {}
    double area()      const override { return M_PI*r*r; }
    double perimeter() const override { return 2*M_PI*r; }
};

class Rectangle : public Shape {
    double w, h;
public:
    Rectangle(string c, double w, double h) : Shape(c), w(w), h(h) {}
    double area()      const override { return w*h; }
    double perimeter() const override { return 2*(w+h); }
};

int main() {
    // Shape s("red"); // ОШИБКА: абстрактный класс!
    
    // Полиморфизм через указатели/ссылки
    vector<unique_ptr<Shape>> shapes;
    shapes.push_back(make_unique<Circle>("красный", 5));
    shapes.push_back(make_unique<Rectangle>("синий", 4, 6));
    shapes.push_back(make_unique<Circle>("жёлтый", 2));
    
    double totalArea = 0;
    for (const auto& s : shapes) {
        s->describe();        // Полиморфный вызов!
        totalArea += s->area();
    }
    cout << "Общая площадь: " << totalArea << endl;
    
    return 0;
}

Итого: что мы узнали

  • ✅ Класс: шаблон с полями (данными) и методами (поведением)
  • ✅ public: доступно всем; private: только внутри; protected: внутри + наследники
  • ✅ Конструктор: инициализирует объект; деструктор (~): освобождает ресурсы
  • ✅ Список инициализации : field(value) — эффективнее чем присваивание в теле
  • ✅ Наследование (class Dog : public Animal): подкласс получает всё от родителя
  • ✅ virtual: функцию можно переопределить в подклассе
  • ✅ override: явно указываем что переопределяем родительский метод
  • ✅ = 0: чисто виртуальная функция → класс становится абстрактным
  • ✅ Виртуальный деструктор обязателен при наследовании!
  • ✅ unique_ptr — умный указатель для безопасного управления памятью