Стислий довідник до іспиту — визначення та концепції, проблема - рішення - результат.
📖 Визначення: ООП — це парадигма програмування, що ґрунтується на концепції "об'єктів" — структур, які поєднують стан (поля) та поведінку (методи) і взаємодіють між собою через передачу повідомлень. Сучасне визначення (ISO/IEC 2382): "підхід до розробки програмного забезпечення, за якого система моделюється як сукупність взаємодіючих об'єктів".
Альтернативні визначення:
- Об'єктно-орієнтоване програмування - це підхід, за якого змінні та функції, що належать до певного об'єкта, об'єднані в коді спеціальним чином і дуже тісно пов'язані між собою.
- В об'єктно-орієнтованому програмуванні - все здійснюється в термінах ОБ'ЄКТІВ, над якими здійснюються дії (об'єкт - центр концепції ООП).
- ООП - це методологія програмування, що ґрунтується на поданні програми у вигляді сукупності об'єктів, кожен з яких є екземпляром певного класу, а класи утворюють ієрархію успадкування.
Клас - це загальний опис стану і поведінки деякої сутності, а також правил взаємодії з нею.
Об'єкт (екземпляр класу) - це окремий представник класу (змінна або константа), що має свій конкретний стан, і поведінку, повністю визначену методами класу.
Поле - це змінна/константа, пов'язана з об'єктом або класом. Стан об'єкта зберігається в його полях. Тип даних поля задається під час опису класу.
Метод - це функція або процедура, що належить об'єкту або класу.
| Проблема | Процедурне програмування розділяє дані та функції, що ускладнює повторне використання коду та моделювання складних систем. |
| Рішення | Представлення програми як сукупності об'єктів, що обмінюються повідомленнями та мають стан і поведінку. |
| Результат | Спрощення інкапсуляції, модульності та повторного використання коду. |
📖 Визначення: Абстрагування — це механізм виділення суттєвих характеристик об'єкта реального світу та ігнорування несуттєвих деталей для цілей моделювання. У 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).
| Проблема | Розробник може заздалегідь не знати, з яким конкретно типом даних працюватиме користувач. |
| Рішення | Єдиний інтерфейс для роботи з різними типами — через перевантаження або віртуальні методи. |
| Результат | Гнучкість коду: «один інтерфейс, багато реалізацій». |
📖 Визначення:
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* |
❌ Ні | Не має сенсу для довільного класу |
📖 Визначення: Статичні члени класу — поля та методи, що належать самому класу, а не конкретному екземпляру. Існують в єдиному екземплярі незалежно від кількості об'єктів. Статичні методи не мають доступу до
thisі викликаються без створення об'єкта:ClassName::method(). Статичні поля (крімconstexpr) потребують визначення поза класом (C++ Standard, §9.4).
| Проблема | Потреба в даних або методах, що належать усьому класу, а не окремим об'єктам (наприклад, лічильник об'єктів). |
| Рішення | Оголошення полів та методів зі специфікатором static. |
| Результат | Глобальні дані в межах класу, доступні без створення екземплярів. |
📖 Визначення: Семантика копіювання визначає, як об'єкт копіюється при ініціалізації (
T b = a) або передачі за значенням. За замовчуванням компілятор генерує поверхневе (побітове) копіювання. Для класів із динамічно виділеною пам'яттю необхідне глибоке копіювання (deep copy). Правило трьох (Rule of Three, C++03): якщо потрібен один із {деструктор, copy constructor, copy operator=} — зазвичай потрібні всі три.
| Проблема | Побітове копіювання об'єктів із покажчиками призводить до того, що два об'єкти вказують на одну пам'ять — помилки при видаленні. |
| Рішення | «Глибоке копіювання» — виділення нової пам'яті для копії даних. |
| Результат | Безпечне копіювання складних об'єктів. |
📖 Визначення: Семантика перенесення (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) — механізм C++ для написання узагальненого коду, параметризованого типами або значеннями. Є формою статичного поліморфізму: код спеціалізується на конкретному типі під час компіляції (template instantiation). Розрізняють: шаблони функцій, шаблони класів, шаблони змінних (C++14) та шаблони псевдонімів (C++11). Концепти (C++20) дозволяють задавати обмеження на параметри шаблонів.
| Проблема | Дублювання ідентичного коду для різних типів даних. |
| Рішення | Параметризація функцій та класів типами даних (статичний поліморфізм). |
| Результат | Універсальний, компактний та безпечний код. |
template<typename T>
T max(T a, T b) { return a > b ? a : b; }📖 Визначення: Асоціація — це найзагальніший тип зв'язку між класами, що описує відношення "використовує" або "знає про". Об'єкти пов'язані логічно, але жоден не є відповідальним за час життя іншого. Може бути однонаправленою або двонаправленою. У коді виражається через покажчики, посилання або поля-значення, але об'єкт-учасник створюється і знищується незалежно (UML 2.5, OMG).
| Проблема | Класи потребують взаємодії між собою, але не мають відносин власності чи залежності по часу життя. |
| Рішення | Один клас зберігає посилання або покажчик на інший; жоден не керує часом життя партнера. |
| Результат | Слабке, гнучке зв'язування між класами без побічних ефектів при знищенні. |
class Teacher; // forward declaration
class Student {
Teacher* teacher; // асоціація: знає про вчителя,
// але не керує його часом життя
public:
Student(Teacher* t) : teacher(t) {}
};📖 Визначення: Агрегування — це спеціалізований тип асоціації, що описує відношення "ціле — частина" (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
};📖 Визначення: Композиція — це сильна форма агрегування, відношення "ціле — невід'ємна частина", за якого частина не може існувати без цілого. Ціле є єдиним власником частини і повністю відповідає за її створення та знищення. У 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 (перевизначення) — надання нової реалізації віртуального методу базового класу у похідному класі з тією самою сигнатурою. Ключове слово
override(C++11) підтверджує намір і дозволяє компілятору виявляти помилки. Shadowing (заміщення/приховування) — оголошення у похідному класі методу з тим самим ім'ям, але іншою сигнатурою; базовий метод стає недоступним без явної кваліфікаціїBase::method(). Shadowing не є поліморфізмом.
| Override | Shadowing | |
|---|---|---|
| Сигнатура | Однакова з базовим | Інша (те саме ім'я) |
| Поведінка | Нова реалізація методу | Приховує батьківський метод |
| Ключове слово | override |
— |
| Тип поліморфізму | Динамічний (через vtable) | Відсутній |
📖 Визначення: Зв'язування (binding) — процес визначення, яка конкретна функція буде викликана за певним викликом. Раннє (статичне) зв'язування відбувається під час компіляції — компілятор підставляє адресу функції безпосередньо. Пізнє (динамічне) зв'язування (C++ Standard, §10.3) відбувається під час виконання через vtable — визначається реальний тип об'єкта, а не тип покажчика чи посилання.
| Раннє (Static) | Пізнє (Dynamic) | |
|---|---|---|
| Коли | Під час компіляції | Під час виконання |
| За типом | Покажчика / посилання | Реального об'єкта |
| Ключове слово | — (за замовчуванням) | virtual |
| Результат | Ігнорує методи спадкоємця | Справжній поліморфізм |
📖 Визначення: 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 — ситуація, коли клас 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📖 Визначення: 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), класи-предикати |
Удачі на іспиті! 🚀