Skip to content

Instantly share code, notes, and snippets.

@sunmeat
Last active June 4, 2026 12:33
Show Gist options
  • Select an option

  • Save sunmeat/c3c629e34ba07f5e515f52d4a4f44cb8 to your computer and use it in GitHub Desktop.

Select an option

Save sunmeat/c3c629e34ba07f5e515f52d4a4f44cb8 to your computer and use it in GitHub Desktop.
бомбічна шпаргалка на іспит з С++

📘 Основи та принципи ООП (на прикладі мови C++)

Стислий довідник до іспиту — визначення та концепції, проблема - рішення - результат.


🧩 Об'єктно-орієнтоване програмування (ООП)

Загальна ідея

📖 Визначення: ООП — це парадигма програмування, що ґрунтується на концепції "об'єктів" — структур, які поєднують стан (поля) та поведінку (методи) і взаємодіють між собою через передачу повідомлень. Сучасне визначення (ISO/IEC 2382): "підхід до розробки програмного забезпечення, за якого система моделюється як сукупність взаємодіючих об'єктів".

Альтернативні визначення:

  1. Об'єктно-орієнтоване програмування - це підхід, за якого змінні та функції, що належать до певного об'єкта, об'єднані в коді спеціальним чином і дуже тісно пов'язані між собою.
  2. В об'єктно-орієнтованому програмуванні - все здійснюється в термінах ОБ'ЄКТІВ, над якими здійснюються дії (об'єкт - центр концепції ООП).
  3. ООП - це методологія програмування, що ґрунтується на поданні програми у вигляді сукупності об'єктів, кожен з яких є екземпляром певного класу, а класи утворюють ієрархію успадкування.

Клас - це загальний опис стану і поведінки деякої сутності, а також правил взаємодії з нею.

Об'єкт (екземпляр класу) - це окремий представник класу (змінна або константа), що має свій конкретний стан, і поведінку, повністю визначену методами класу.

Поле - це змінна/константа, пов'язана з об'єктом або класом. Стан об'єкта зберігається в його полях. Тип даних поля задається під час опису класу.

Метод - це функція або процедура, що належить об'єкту або класу.

Проблема Процедурне програмування розділяє дані та функції, що ускладнює повторне використання коду та моделювання складних систем.
Рішення Представлення програми як сукупності об'єктів, що обмінюються повідомленнями та мають стан і поведінку.
Результат Спрощення інкапсуляції, модульності та повторного використання коду.

🔷 Абстрагування

📖 Визначення: Абстрагування — це механізм виділення суттєвих характеристик об'єкта реального світу та ігнорування несуттєвих деталей для цілей моделювання. У C++ реалізується через класи, абстрактні класи та інтерфейси (Grady Booch, "Object-Oriented Analysis and Design", 1994).

Проблема Складність об'єктів навколишнього світу заважає їх моделюванню в програмі.
Рішення Виділення лише значущих характеристик об'єкта та ігнорування другорядних.
Результат Отримання простішої та зрозумілої моделі (класу) для боротьби зі складністю.

🔷 Інкапсуляція

📖 Визначення: Інкапсуляція — це принцип об'єднання даних і методів, що оперують цими даними, в єдину одиницю (клас), а також приховування внутрішньої реалізації від зовнішнього коду. Забезпечується специфікаторами доступу: public, private, protected (C++ Standard, ISO/IEC 14882).

Проблема Необхідність захистити дані від некоректного використання та відокремити деталі реалізації від інтерфейсу.
Рішення Об'єднання полів і методів у єдину сутність (клас) з доступом через специфікатори public, private.
Результат Змінність внутрішньої реалізації без порушення клієнтського коду («чорна скринька»).

🔷 Успадкування

📖 Визначення: Успадкування — це механізм, що дозволяє новому класу (похідному) набувати властивостей та поведінки вже існуючого класу (базового). Реалізує відношення "є різновидом" (is-a). Принцип підстановки Лісков (LSP, Barbara Liskov, 1987): об'єкт похідного класу повинен бути замінним об'єктом базового без порушення коректності програми.

Проблема (технічна) Дублювання коду при створенні схожих сутностей.
Проблема (логічна) Необхідність розглядати об'єкт похідного типу як об'єкт базового.
Рішення Механізм створення нових класів на основі існуючих із можливістю розширення функціоналу.
Результат Повторне використання коду та створення ієрархій об'єктів.

🔷 Поліморфізм

📖 Визначення: Поліморфізм (від грец. "багато форм") — здатність об'єктів різних класів реагувати на одні й ті самі виклики методів по-різному. У C++ розрізняють: статичний поліморфізм (шаблони, перевантаження функцій — вирішується під час компіляції) та динамічний поліморфізм (віртуальні методи — вирішується під час виконання через vtable).

Проблема Розробник може заздалегідь не знати, з яким конкретно типом даних працюватиме користувач.
Рішення Єдиний інтерфейс для роботи з різними типами — через перевантаження або віртуальні методи.
Результат Гнучкість коду: «один інтерфейс, багато реалізацій».

⚙️ Технічні особливості класів у C++

Покажчик this

📖 Визначення: this — це неявний константний покажчик (T* const this), що автоматично передається у кожен нестатичний метод класу та вказує на поточний об'єкт, для якого викликано метод. Для const-методів тип — const T* const. Не існує для статичних методів (C++ Standard, §9.3.2).

Проблема Метод класу один, а об'єктів багато; метод повинен знати, з полями якого саме об'єкта він працює.
Рішення Неявна передача константного покажчика на поточний об'єкт як нульового параметра у кожен нестатичний метод.
Результат Можливість ідентифікувати об'єкт та розв'язувати конфлікти імен.

Список ініціалізації

📖 Визначення: Список ініціалізації членів (member initializer list) — спеціальний синтаксис конструктора у C++, що дозволяє ініціалізувати поля класу до початку виконання тіла конструктора. Це єдиний спосіб ініціалізувати: поля-посилання, const-поля, поля без конструктора за замовчуванням та базові класи (C++ Standard, §12.6.2). Ініціалізація через список ефективніша за присвоювання в тілі.

Проблема Необхідність ініціалізувати поля-посилання або об'єкти класів без конструктора за замовчуванням.
Рішення Спеціальний синтаксис у конструкторі для ініціалізації полів до виконання його тіла.
Результат Гарантована і коректна ініціалізація специфічних типів полів.
class Foo {
    int& ref;
    Bar bar;
public:
    Foo(int& r, Bar b) : ref(r), bar(b) { /* тіло */ }
};

Порожній клас — методи за замовчуванням

📖 Визначення: Клас у C++ — тип даних, визначений користувачем, що об'єднує дані (поля) та функції (методи). Компілятор C++11 автоматично генерує 6 спеціальних методів, якщо вони не визначені явно (C++ Standard, §12). Правило п'яти (Rule of Five, C++11): якщо потрібен один із {деструктор, copy/move constructor, copy/move operator=}, зазвичай потрібні всі п'ять.

Проблема Об'єкт класу повинен поводитися як стандартний тип, навіть якщо програміст не написав жодного методу.
Рішення Компілятор автоматично генерує необхідні методи.
Результат Базова функціональність доступна одразу після оголошення класу.

Компілятор C++ автоматично генерує 6 методів:

Метод Генерується? Примітка
Конструктор за замовчуванням Так Якщо не оголошено жодного конструктора
Конструктор копіювання Так
Конструктор переміщення Так (C++11) Якщо не оголошено copy/move/деструктор
Оператор копіювального присвоювання Так
Оператор переміщувального присвоювання Так (C++11)
Деструктор Так
new / new[] / delete / delete[] ❌ Ні Використовується глобальний ::operator
operator& ❌ Ні Компілятор бере адресу вбудованим механізмом
operator* ❌ Ні Не має сенсу для довільного класу

Статичні члени класу static

📖 Визначення: Статичні члени класу — поля та методи, що належать самому класу, а не конкретному екземпляру. Існують в єдиному екземплярі незалежно від кількості об'єктів. Статичні методи не мають доступу до this і викликаються без створення об'єкта: ClassName::method(). Статичні поля (крім constexpr) потребують визначення поза класом (C++ Standard, §9.4).

Проблема Потреба в даних або методах, що належать усьому класу, а не окремим об'єктам (наприклад, лічильник об'єктів).
Рішення Оголошення полів та методів зі специфікатором static.
Результат Глобальні дані в межах класу, доступні без створення екземплярів.

🗂️ Семантика та керування ресурсами

Семантика копіювання

📖 Визначення: Семантика копіювання визначає, як об'єкт копіюється при ініціалізації (T b = a) або передачі за значенням. За замовчуванням компілятор генерує поверхневе (побітове) копіювання. Для класів із динамічно виділеною пам'яттю необхідне глибоке копіювання (deep copy). Правило трьох (Rule of Three, C++03): якщо потрібен один із {деструктор, copy constructor, copy operator=} — зазвичай потрібні всі три.

Проблема Побітове копіювання об'єктів із покажчиками призводить до того, що два об'єкти вказують на одну пам'ять — помилки при видаленні.
Рішення «Глибоке копіювання» — виділення нової пам'яті для копії даних.
Результат Безпечне копіювання складних об'єктів.

Семантика перенесення (Move semantics)

📖 Визначення: Семантика перенесення (C++11) — механізм, що дозволяє переміщувати ресурси з одного об'єкта в інший замість їх копіювання. Спирається на rvalue-посилання (&&). Rvalue — тимчасовий об'єкт або значення без імені (xvalue, prvalue за класифікацією C++11, §3.10). Конструктор переміщення "краде" ресурси у вихідного об'єкта, залишаючи його у валідному, але невизначеному стані. Розширює Rule of Three до Rule of Five.

Проблема Зайві витрати ресурсів на копіювання тимчасових об'єктів.
Рішення Конструктор перенесення та && (rvalue-посилання) — «забирання» ресурсів у тимчасового об'єкта.
Результат Значне підвищення продуктивності при роботі з великими об'єктами.
MyClass(MyClass&& other) noexcept {
    data = other.data;
    other.data = nullptr; // "забираємо" ресурс
}

Перевантаження операцій

📖 Визначення: Перевантаження операторів (operator overloading) — можливість C++ надавати власну реалізацію вбудованих операторів (+, -, ==, [] тощо) для типів, визначених користувачем. Перевантажений оператор — це функція зі спеціальною назвою operator@. Не можна перевантажити: ::, .*, ., ?:, sizeof, typeid (C++ Standard, §13.5).

Спосіб Коли використовувати Приклад
Метод класу Змінює об'єкт; лівий операнд — this operator+=
Глобальна функція Повертає нове значення; обидва операнди рівноправні operator+
Проблема Стандартні оператори (+, -, ==) не працюють із користувацькими типами.
Рішення Визначення власних дій для операторів через методи класу або глобальні (дружні) функції.
Результат Краща читаність коду та інтеграція класів в інфраструктуру мови (шаблони, STL).

Шаблони templates

📖 Визначення: Шаблони (templates) — механізм C++ для написання узагальненого коду, параметризованого типами або значеннями. Є формою статичного поліморфізму: код спеціалізується на конкретному типі під час компіляції (template instantiation). Розрізняють: шаблони функцій, шаблони класів, шаблони змінних (C++14) та шаблони псевдонімів (C++11). Концепти (C++20) дозволяють задавати обмеження на параметри шаблонів.

Проблема Дублювання ідентичного коду для різних типів даних.
Рішення Параметризація функцій та класів типами даних (статичний поліморфізм).
Результат Універсальний, компактний та безпечний код.
template<typename T>
T max(T a, T b) { return a > b ? a : b; }

🔗 Зв'язки між класами

Асоціація (Association)

📖 Визначення: Асоціація — це найзагальніший тип зв'язку між класами, що описує відношення "використовує" або "знає про". Об'єкти пов'язані логічно, але жоден не є відповідальним за час життя іншого. Може бути однонаправленою або двонаправленою. У коді виражається через покажчики, посилання або поля-значення, але об'єкт-учасник створюється і знищується незалежно (UML 2.5, OMG).

Проблема Класи потребують взаємодії між собою, але не мають відносин власності чи залежності по часу життя.
Рішення Один клас зберігає посилання або покажчик на інший; жоден не керує часом життя партнера.
Результат Слабке, гнучке зв'язування між класами без побічних ефектів при знищенні.
class Teacher; // forward declaration

class Student {
    Teacher* teacher; // асоціація: знає про вчителя,
                      // але не керує його часом життя
public:
    Student(Teacher* t) : teacher(t) {}
};

Агрегування (Aggregation)

📖 Визначення: Агрегування — це спеціалізований тип асоціації, що описує відношення "ціле — частина" (has-a / part-of), за якого частина може існувати незалежно від цілого. Ціле "містить" частини, але не є відповідальним за їх створення та знищення. У C++ виражається через покажчики або посилання на зовнішні об'єкти. У UML позначається ромбом без заливки (UML 2.5, OMG).

Проблема Потрібно змоделювати відношення "містить", де частини можуть існувати і використовуватись поза контекстом цілого.
Рішення Клас-ціле зберігає покажчики/посилання на об'єкти-частини, які створені і знищуються зовні.
Результат Гнучке відношення "ціле — частина" без жорсткої прив'язки часу життя.
class Engine { /* ... */ };

class Car {
    Engine* engine; // агрегування: Car містить Engine,
                    // але Engine існує незалежно
public:
    Car(Engine* e) : engine(e) {}
    // Engine НЕ видаляється у деструкторі Car
};

Композиція (Composition)

📖 Визначення: Композиція — це сильна форма агрегування, відношення "ціле — невід'ємна частина", за якого частина не може існувати без цілого. Ціле є єдиним власником частини і повністю відповідає за її створення та знищення. У C++ виражається через об'єкти-члени за значенням (by value) або unique_ptr. У UML позначається ромбом із заливкою. Відповідає патерну RAII (C++ Standard; Bjarne Stroustrup, "The C++ Programming Language").

Проблема Потрібно змоделювати відношення, де частина є невід'ємною складовою цілого і не має сенсу без нього.
Рішення Клас-ціле зберігає частини за значенням або через unique_ptr; повністю керує їх часом життя.
Результат Жорсткий зв'язок і автоматичне управління ресурсами — при знищенні цілого знищуються всі частини.
class Heart { /* ... */ };

class Human {
    Heart heart; // композиція: Heart — невід'ємна частина Human,
                 // знищується разом із Human автоматично
public:
    Human() : heart() {}
};

Порівняльна таблиця зв'язків

Тип Відношення Час життя частини C++ вираз UML
Асоціація "використовує" Незалежний T* / T& (зовнішній) Стрілка
Агрегування "містить" (слабке) Незалежний T* (зовнішній) Ромб ◇
Композиція "складається з" (сильне) Залежний від цілого T (by value) / unique_ptr<T> Ромб ◆

🔗 Динамічний поліморфізм та зв'язування

Перевизначення (override) vs Заміщення (shadowing)

📖 Визначення: Override (перевизначення) — надання нової реалізації віртуального методу базового класу у похідному класі з тією самою сигнатурою. Ключове слово override (C++11) підтверджує намір і дозволяє компілятору виявляти помилки. Shadowing (заміщення/приховування) — оголошення у похідному класі методу з тим самим ім'ям, але іншою сигнатурою; базовий метод стає недоступним без явної кваліфікації Base::method(). Shadowing не є поліморфізмом.

Override Shadowing
Сигнатура Однакова з базовим Інша (те саме ім'я)
Поведінка Нова реалізація методу Приховує батьківський метод
Ключове слово override
Тип поліморфізму Динамічний (через vtable) Відсутній

Раннє та пізнє зв'язування

📖 Визначення: Зв'язування (binding) — процес визначення, яка конкретна функція буде викликана за певним викликом. Раннє (статичне) зв'язування відбувається під час компіляції — компілятор підставляє адресу функції безпосередньо. Пізнє (динамічне) зв'язування (C++ Standard, §10.3) відбувається під час виконання через vtable — визначається реальний тип об'єкта, а не тип покажчика чи посилання.

Раннє (Static) Пізнє (Dynamic)
Коли Під час компіляції Під час виконання
За типом Покажчика / посилання Реального об'єкта
Ключове слово — (за замовчуванням) virtual
Результат Ігнорує методи спадкоємця Справжній поліморфізм

Віртуальні методи та V-таблиця

📖 Визначення: Vtable (таблиця віртуальних методів) — структура даних, що генерується компілятором для кожного класу з віртуальними методами. Містить масив покажчиків на актуальні реалізації методів. Кожен об'єкт такого класу містить прихований покажчик vptr, що вказує на vtable свого класу. Виклик obj->method() через базовий покажчик перетворюється на *(obj->vptr[N])(). Vtable — де-факто стандарт реалізації, використовується всіма мейнстрімними компіляторами.

Проблема Потрібно викликати правильну реалізацію методу через покажчик базового класу.
Рішення Таблиця віртуальних методів (vtable) та прихований покажчик (vptr) в об'єкті.
Результат Динамічне розв'язання виклику залежно від реального типу об'єкта.
Об'єкт у пам'яті:
┌──────────┐
│  vptr ───┼──► vtable: [methodA*, methodB*, ...]
│  field1  │
│  field2  │
└──────────┘

Чисто віртуальні методи та Абстрактні класи

📖 Визначення: Чисто віртуальний метод (pure virtual function) — метод, оголошений як virtual f() = 0;, що не має реалізації у даному класі та зобов'язує похідні класи надати власну. Клас із хоча б одним чисто віртуальним методом є абстрактним (abstract class): не можна створити екземпляр такого класу безпосередньо. Використовується для визначення загального інтерфейсу ієрархії (C++ Standard, §10.4).

Проблема Базовий клас описує лише концепцію і не може надати логічну реалізацію (наприклад, Фігура::Малювати).
Рішення Оголошення методу як virtual ... = 0; — клас стає абстрактним.
Результат Неможливість створити об'єкт абстрактного типу; спадкоємці зобов'язані реалізувати методи.
class Shape {
public:
    virtual void draw() = 0; // чисто віртуальний
};

Інтерфейс

📖 Визначення: Інтерфейс у C++ — це абстрактний клас, що містить виключно публічні чисто віртуальні методи та (обов'язково) віртуальний деструктор. Задає "контракт" — набір операцій, які зобов'язані реалізувати всі класи-спадкоємці. На відміну від Java/C#, C++ не має окремого синтаксису для інтерфейсів. Підтримка кількох інтерфейсів реалізується через множинне успадкування. Конвенція іменування: префікс I (IDrawable, ISerializable).

Проблема Потреба встановити набір обов'язкових дій (контракт) для непов'язаних класів.
Рішення Клас, що містить лише декларації публічних чисто віртуальних методів.
Результат Максимальна абстракція; один клас може реалізувати кілька «контрактів».
class IDrawable {
public:
    virtual void draw() = 0;
    virtual ~IDrawable() = default;
};

💎 Множинне успадкування

Проблема ромба (Diamond Problem)

📖 Визначення: Множинне успадкування — можливість похідного класу успадковувати від двох і більше базових класів одночасно. Diamond problem — ситуація, коли клас D успадковує від B і C, обидва з яких успадковують від A: без спеціальних заходів D міститиме дві копії членів A, і виникає неоднозначність. Рішення C++: virtual inheritance (§10.1) гарантує єдину спільну копію базового підоб'єкта A у D. Деструктор A слід оголошувати virtual.

Проблема При множинному успадкуванні від двох+ класів зі спільним предком — дублювання полів предка та неоднозначність.
Рішення Використання віртуального базового класу: virtual public Base.
Результат Гарантується лише одна копія базового об'єкта в ієрархії.
      A
     / \
    B   C      ← без virtual: у D є дві копії A
     \ /
      D
class B : virtual public A { };
class C : virtual public A { };
class D : public B, public C { }; // одна копія A

📦 Standard Template Library (STL)

📖 Визначення: STL (Standard Template Library) — частина стандартної бібліотеки C++, що надає узагальнені контейнери, алгоритми та ітератори. Розроблена Олександром Степановим (1994) і стандартизована як частина C++98. Ґрунтується на принципі розділення структур даних та алгоритмів через концепцію ітераторів. Більшість компонентів STL реалізовані як шаблони і мають складність, гарантовану стандартом (наприклад, std::sort — O(n log n)).

Проблема Необхідність вручну писати структури даних та алгоритми для кожного проекту.
Рішення Використання стандартних шаблонів STL.
Результат Уніфікація коду, висока продуктивність та швидка розробка.
Артефакт Опис Приклади
Контейнери Керують наборами даних у пам'яті vector, list, map, unordered_map
Адаптери Змінюють інтерфейс контейнерів stack, queue, priority_queue
Ітератори Узагальнені покажчики для уніфікованого доступу begin(), end(), rbegin()
Алгоритми Обчислювальні процедури, відокремлені від структур sort, find, transform, copy
Функтори / Предикати Об'єкти з operator() — передача логіки в алгоритми Лямбди (C++11), класи-предикати

Удачі на іспиті! 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment