
- •Міністерство надзвичайних ситуацій України Львівський державний університет безпеки життєдіяльності Юрій грицюк, Тарас рак
- •Навчальний посібник
- •Потреба використання об'єктно-орієнтованого програмування
- •Поняття про об’єктно-орієнтований підхід до розроблення складних програм
- •Основні компоненти об’єктно-орієнтованої мови програмування
- •Поняття про універсальну мову моделювання
- •Базові поняття класу
- •Код програми 2.1. Демонстрація механізму оголошення класу та його застосування
- •Поняття про конструктори і деструктори
- •Особливості реалізації механізму доступу до членів класу
- •Класи і структури - споріднені типи
- •Об'єднання та класи - споріднені типи
- •Поняття про вбудовані функції
- •Inline int myClass::Put() return c;
- •Особливості організації масивів об'єктів
- •Особливості використання покажчиків на об'єкти
- •Поняття про функції-"друзі" класу
- •Код програми 3.1. Демонстрація механізму використання "дружньої" функції для доступу до закритих членів класу
- •Код програми 3.2. Демонстрація механізму використання "дружньої" функції для перевірки статусу кожного об'єкта
- •Void Run(); //Таймер відліку часу
- •Void timerClass::Run()
- •Int mainO
- •Особливості механізму динамічної ініціалізації конструктора
- •Int s; public:
- •Void Run(); //Таймер відліку часу
- •Void timerClass::Run()
- •Int mainO
- •Особливості механізму присвоєння об'єктів
- •Int а, ь; public:
- •Int mainO
- •Особливості механізму передачі об'єктів функціям
- •Void Fun(myClassobj)
- •Int mainO
- •Конструктори, деструктори і передача об'єктів
- •Void Get(myClass obj)
- •Int mainO
- •Потенційні проблеми, які виникають при передачі об'єктів
- •Int *р; public:
- •Void Get(myClass &obj) // Передача об'єкта за посиланням
- •Int mainO
- •Особливості механізму повернення об'єктів функціями
- •Void Set(char*s) {strcpy(str, s);}
- •Void Show() {cout «"Рядок:" « str« endl;}
- •Int mainO
- •Int mainO
- •Механізми створення та використання конструктора копії
- •Використання конструктора копії для ініціалізації одного об'єкта іншим
- •Int mainO
- •Механізм використання конструктора копії для передачі об'єкта функції
- •Int mainO
- •Механізм використання конструктора копії при поверненні функцією об'єкта
- •Int mainO
- •3.7.4. Конструктори копії та їх альтернативи
- •Поняття про ключове слово this
- •Void Fun() {...};
- •Int mainO
- •Механізми перевизначення операторів з використанням функцій-членів класу
- •Int х, у, z; //Тривимірні координати
- •Int mainO
- •Intх,у,z; //Тривимірні координати
- •Void Show(char*s);
- •Int mainO
- •Int х, у, z; //Тривимірні координати
- •Int mainO
- •Особливості реалізації механізму перевизначення операторів
- •Механізми иеревизначення операторів з використанням функцій-не членів класу
- •Використання функцій-"друзів" класу для перевизначення бінарних операторів
- •Void Show(char*s);
- •Int mainO
- •Int mainO
- •Використання функцій-"друзів" класу для перевизначення унарних операторів
- •Int mainO
- •Особливості реалізації оператора присвоєння
- •Int mainO
- •Int mainO
- •Механізми перевизначення оператора індексації елементів масиву "[]"
- •Int mainO
- •Int aMas[size]; public:
- •Int mainO
- •Int aMas[size]; public:
- •Int mainO
- •Механізми перевизначення оператора виклику функцій "()"
- •Int mainO
- •Механізми перевизначення рядкових операторів
- •Конкатенація та присвоєння класу рядків з рядками класу
- •Void Show(char*s) {cout« s « string « endl;}
- •Конкатенація та присвоєння класу рядків з рядками, що закінчуються нульовим символом
- •Void Show(char*s) {cout« s « string « endl;}
- •Void Show(char*s) {cout« s « string « endl;}
- •Int mainO
- •Поняття про успадкування в класах
- •Int kolesa; // Кількість коліс int pasagyr; // Кількість пасажирів public:
- •Int mistkist; // Вантажомісткість у м куб. Public:
- •Int kolesa; // Кількість коліс int pasagyr; // Кількість пасажирів public:
- •Int mainO
- •Використання специфікатора доступу protected для надання членам класу статусу захищеності
- •Int mainO
- •Int mainO
- •Int d; // Захищений public:
- •Int mainO
- •Protected-членом класу derived, що робить його недоступним за його межами. */
- •Void showX() {cout« х « endl;}
- •Void showY() {cout« у « endl;}
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Успадкування віртуальних функцій
- •Void Show() {cout« Суг("Другий похідний клас.") « endl;}
- •Virtual void Show() {cout« Суг("Базовий клас.") « endl;}
- •Void Show() {cout« Суг("Перший похідний клас. ")« endl;}
- •Int mainO
- •Virtual void ShowO {cout« Суг("Базовий клас.") « endl;}
- •Void Show() {cout« Суг("Перший похідний клас. ")« endl;}
- •Int mainO
- •Void Show()
- •Void Show()
- •Int mainO
- •Void Show()
- •Void Show()
- •Int mainO
- •Void swapAb(aType &а, аТуре &b)
- •Int mainO
- •Приклад створення узагальненого класу для організації безпечного масиву
- •Int mainO
- •Використання в узагальнених класах аргументів, що не є узагальненими типами
- •Використання в шаблонних класах аргументів за замовчуванням
- •Int mainO
- •Механізм реалізації безпосередньо заданої спеціалізації класів
- •Int mainO
- •Основні особливості оброблення виняткових ситуацій
- •Системні засоби оброблення винятків
- •Xtest(1);
- •Xtest(2);
- •Перехоплення винятків класового типу
- •Використання декількох catch-наетанов
- •Варіанти оброблення винятків
- •Перехоплення всіх винятків
- •Накладання обмежень на тип винятків, які генеруються функціями
- •Int mainO
- •Xhandler(o); // Спробуйте також передати функції XhandlerO аргументи 1 і 2.
- •Void Xhandler(int test) throw 0
- •Повторне генерування винятку
- •Int mainO
- •Int mainO
- •Механізми перевизначення операторів new і delete
- •Void *р;
- •Void *p;
- •Int mainO
- •Класи потоків
- •Особливості механізмів перевизначення операторів введення-виведення даних
- •Створення перевюначених операторів виведення даних
- •Int mainO
- •Використання функцій-"друзів" класу для перевюначення операторів виведення даних
- •Int х, у, z; //Тривимірні координати (тепер це private-члени) public:
- •Int mainO
- •Створення перевюначених операторів введення даних
- •Istream &operator»(istream &stream, kooClass &obj)
- •Cout«"Введіть координати X, у і z:
- •Stream » obj.X » obj.Y » obj.Z;
- •Istream &operator»(istream &stream, objectType &obj)
- •// Код операторної функції введення даних
- •Class kooClass {// Оголошення класового типу int х, у, z; // Тривимірні координати
- •Istream &operator»(istream &stream, kooClass &obj)
- •Int mainO
- •Void unsetf(fmtflags flags),
- •Void showflags(ios::fmtflags f); // Відображення поточного стану опцій
- •Int mainO
- •Ios::fmtflags f; // Оголошення параметру для поточного стану опцій
- •Int mainO
- •Створення власних маніиуляторних функцій
- •Організація файлового введення-виведення даних
- •Відкриття та закриття файлу
- •Зчитування та запис текстових файлів
- •Ifstream in(argv[1], ios::in | ios::binary); if(!in){
- •In.CloseO; getchO; return 0;
- •Int mainO
- •Зчитування та записування у файл блоків даних
- •Int mainO
- •Ifstream inftest", ios::in | ios::binary); if(!in){
- •Використання функції eof() для виявлення кінця файлу
- •If(!in.Eof()) cout« ch;
- •In.CloseO; getchO; return 0;
- •Int main(int arge, char *argv[])
- •Ifstream f1(argv[1], ios::in | ios::binary); if(!f1) {
- •Ifstream f2(argv[2], ios::in | ios::binary);
- •Int main(int arge, char *argv[])
- •Ifstream in(argv[1], ios::in | ios::binary); if(!in){
- •In.Seekg(atoi(argv[2]), ios::beg); while(in.Get(ch)) cout« ch; getchO; return 0;
- •Int х, у, z; // Тривимірні координати; вони тепер закриті public:
- •Int mainO
- •Virtual void Fun() {}; // Робимо клас Base поліморфним
- •Int mainO
- •Virtual void FunO {cout«"у класі Base"« endl;}
- •Int mainO
- •Virtual void FunO {}
- •Void derivedOnlyO {cout«"Це об'єкт класу Derived"« endl;}
- •Int mainO
- •Void Fun(const int *р)
- •Int mainO
- •Namespace ns { int d;
- •Застосування настанови using
- •Int Comp(const void *а, const void *b);
- •Int mainO
- •Int Comp(const void *a, const void *b)
- •Int mainO
- •Int Fun10 const; // const-функція-член
- •Int PutO const; return c; // Все гаразд
- •Int mainO
- •0Bj.Set(1900); cout « Obj.PutO;
- •Void setJ(int х) const // Наступна функція не відкомпілюється.
- •Int а; public:
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int myClass::*dp; // Покажчик на int-члена класу void (myClass::*fp)(int X); // Покажчик на функцію-члена
- •Int mainO
- •Механізми роботи з векторами
- •Int mainO
- •1 234 5 678 9 10 11 12 13141516 17 1819 Вміст подвоєно:
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Int mainO
- •Символи н
- •Символів представляють голосні звуки.
- •Int mainO
- •Int mainO
- •Int mainO
- •Void getaLine(string& inStr); // Отримання рядка тексту char getaCharO; //Отримання символу
- •Int aptNumber; // Номер кімнати мешканця
- •Void DisplayO; // Виведення переліку мешканців
- •Int aptNo; float rent[12]; public:
- •Void setRent(int, float); // Запис плати за місяць
- •Void insertRent(int, int, float); void DisplayO;
- •Int month, day; string category, payee; float amount; expense() {}
- •Int mainO
- •Void rentRecord::insertRent(int aptNo, int month, float amount)
- •SetPtrsRr.Insert(ptrRow); // Занести рядок вектор
- •If( setPtrsRr.Empty())
- •Else // Інакше
- •Int month, day; string category, payee; float amount;
- •«" 'Є' для запису витрат:";
- •Навчальний посібник
Virtual void Fun() {}; // Робимо клас Base поліморфним
.
};
class DerivedA: public Base {
.
};
class DerivedB: public Base {
.
};
Int mainO
{
Base *p, baseob;
DerivedA ObjA; // Створення об'єкта класу DerivedB ObjB; // Створення об'єкта класу
р = &baseob;
cout«"Змінна р вказує на об'єкт типу cout«typeid(*p).name()« endl;
р = &0bjA;
cout«"Змінна p вказує на об'єкт типу cout«typeid(*p).name()« endl;
р = &0bjB;
cout«"Змінна p вказує на об'єкт типу"; cout«typeid(*p).name()« endl;
getchO; return 0;
}
Ось як виглядають результати виконання цієї програми:
Змінна р вказує на об'єкт типу Base Змінна р вказує на об'єкт типу DerivedA Змінна р вказує на об'єкт типу DerivedB
Якщо оператор typeid застосовується до покажчика на базовий клас поліморфного типу, то тип об'єкта, який реально адресується, як підтверджують ці результати, буде визначений у процесі виконання програми.
У всіх випадках застосування оператора typeid до покажчика на неполімор- фну ієрархію класів буде отримано покажчик на базовий тип, тобто те, на що цей покажчик реально вказує, визначити не можна. Як експеримент спробуйте перетворити на коментар віртуальну функцію Fun() у класі Base і подивіться на результат. Ви побачите, що тип кожного об'єкта після внесення у програму цієї зміни буде визначений як Base, оскільки саме цей тип має покажчик р.
Оскільки оператор typeid зазвичай застосовується до перейменованого покажчика (тобто до покажчика, до якого вже застосовано оператор "*"), то для оброблення ситуації, коли цей перейменований покажчик виявиться нульовим, створено спеціальний виняток. У цьому випадку оператор typeid генерує виняток типу bad_typeid.
Посилання на об'єкти ієрархії поліморфних класів працюють подібно до покажчиків. Якщо оператор typeid застосовується до посилання на поліморфний клас, то він повертає тип об'єкта, на який вона реально посилається, і це може бути об'єкт не базового, а похідного типу. Описаний засіб найчастіше використовують при передачі об'єктів функціям за посиланням. Наприклад, у наведеному нижче коді програми функція WhatTypeO оголошує посилальний параметр на об'єкти типу Base. Це означає, що функції WhatType() можна передавати посилання на об'єкти типу Base або посилання на об'єкти будь-яких класів, похідних від Base. Оператор typeid, що застосовується до такого параметра, поверне реальний тип об'єкта, який передається функції.
Код програми 10.3. Демонстрація механізму застосування оператора typeid до посилального параметра #include <iostream> // Для потокового введення-виведення
#include <typeinfo> // Для динамічної ідентифікації типів
using namespace std; // Використання стандартного простору імен
// Оголошення базового класу class Base {
virtual void FunO {; // робимо клас Base поліморфним
.
};
class DerivedA: public Base {
.
};
class DerivedB: public Base {
.
};
// Демонструємо застосування оператора typeid до посилального параметра, void WhatType(Base &obj)
{
cout«"Параметр obj посилається на об'єкт типу cout«typeid(obj).nameO « endl;
}
int mainO
{
int c;
Base baseob;
DerivedA ObjA; // Створення об'єкта класу
DerivedB ObjB; // Створення об'єкта класу
WhatType (baseob);
WhatType(ObjA);
WhatType(ObjB);
getchO; return 0;
}
Внаслідок виконання ця програма відображає на екрані такі результати: Параметр obj посилається на об'єкт типу Base Параметр obj посилається на об'єкт типу DerivedA Параметр obj посилається на об'єкт типу DerivedB
Існує ще одна версія застосування оператора typeid, яка як аргумент приймає ім'я типу. Формат його є таким: іуреЩм'я_типа)
Наприклад, наступна настанова абсолютно допускається: cout«typeid(int).name();
Призначення цієї версії оператора typeid - отримати об'єкт типу type_info (який описує заданий тип даних), щоб його можна було використовувати в настанові порівняння типів.
Приклад RTTI-застосування
У наведеному нижче коді програми показано, наскільки корисним може бути засіб динамічної ідентифікації типів (RTTI). Тут використовується модифікована версія ієрархії класів геометричних фігур з розд. 6 [9], який обчислює площу круга, трикутника і прямокутника. У наведеному нижче коді програми визначено функцію factory(), призначену для створення примірника круга, трикутника або прямокутника. Ця функція повертає покажчик на створений об'єкт1. Конкретний тип створюваного об'єкта визначається внаслідок звернення до функції rand() С++-ге- нератора випадкових чисел. Таким чином, ми не можемо знати наперед, об'єкт якого типу буде згенеровано. Програма створює десять об'єктів і підраховує кількість створених фігур кожного типу. Оскільки під час виклику функції factory() може бути згенерована фігура будь-якого типу, то для визначення типу реально створеного об'єкта у програмі використовують оператор typeid.
Код програми 10.4. Демонстрація ефективності використання засобу динамічної ідентифікації типів #include <iostream> // Для потокового введення-виведення
#include <cstdlib> // Для використання бібліотечних функцій
using namespace std; // Використання стандартного простору імен
class figure { protected:
double х, у; public:
figure(double _x, double _y) {x = _x; у = _y;} virtual double area() = 0;
};
class triangle: public figure { public:
triangle(double _x, double _y): figure(_x, _y) {} double areaO {return x * 0.5 * y;}
};
class rectangle: public figure { public:
rectangle(double _x, double _y): figure(_x, _y) {} double areaO {return x * y;}
};
class circle: public figure { public:
circle(double _x, double _y=0): figure(_x, _y) {} double areaO {return 3.14 * x * x;}
};
// Генератор об'єктів класу figure, figure *factoryO {
switch(rand() % 3) {
case 0: return new circle(10.0); case 1: return new triangle(10.1,5.3); case 2: return new rectangle(4.3, 5.7);
}
return 0;
}
int mainO
{
figure *p; // Покажчик на базовий клас
int t = 0, r = 0, с = 0;
// Генеруємо і підраховуємо об'єкти for(int і=0; і<10; І++) {
р = factoryO; //Генеруємо об'єкт
cout«"Об'єкт має тип" «typeid(*p).name();
cout«
// Враховуємо цей об'єкт if(typeid(*p) == typeid(triangle)) t++; if(typeid(*p) == typeid(rectangle)) r++; if(typeid(*p) == typeid(circle)) C++;
// Відображаємо площу фігури
cout«"Площа дорівнює" « p->area() « endl;
}
cout« endl;
cout«"Згенеровано такі об'єкти:"« endl; cout«" трикутників:" «t« endl; cout«" прямокутників:" «r« endl; cout«" кругів:" « c « endl;
getchO; return 0;
}
Можливий результат виконання цієї програми такий:
Об'єкт має тип class rectangle. Площа дорівнює 24.51 Об'єкт має тип class rectangle. Площа дорівнює 24.51 Об'єкт має тип class triangle. Площа дорівнює 26.765 Об'єкт має тип class triangle. Площа дорівнює 26.765 Об'єкт має тип class rectangle. Площа дорівнює 24.51 Об'єкт має тип class triangle. Площа дорівнює 26.765 Об'єкт має тип class circle. Площа дорівнює 314 Об'єкт має тип class circle. Площа дорівнює 314 Об'єкт має тип class triangle. Площа дорівнює 26.765 Об'єкт має тип class rectangle. Площа дорівнює 24.51
Згенеровано такі об'єкти: трикутників: 4 прямокутників: 4 кругів: 2
10.1.3. Застосування оператора typeid до шаблонних класів
Оператор typeid можна застосувати і до шаблонних класів. Тип об'єкта, який є примірником шаблонного класу, визначається частково на підставі того, які саме дані використовуються для його узагальнених даних під час реалізації об'єкта. Таким чином, два примірники одного і того ж шаблонного класу, які створюються з використанням різних даних, мають різний тип. Розглянемо простий приклад.
Код програми 10.5. Демонстрація механізму застосування оператора typeid до шаблонних класів #include <iostream> // Для потокового введення-виведення
#include <cstdlib> // Для використання бібліотечних функцій
using namespace std; // Використання стандартного простору імен
template <class myClass> class myClass { // Оголошення класового типу myClass a; public:
myClass(myClass c) {a = c;}
//...
};
int mainO
{
myClass<int> ObjA(IO), ObjB(9); myClass<double> ObjC(7.2);
cout«"Об'єкт ObjA має тип cout«typeid(ObjA).name() « endl;
cout«"Об'єкт ObjB має тип cout«typeid(ObjB).name() « endl;
cout«"Об'єкт ObjC має тип"; cout«typeid(ObjC).nameO « endl;
cout« endl;
if(typeid(ObjA) == typeid(ObjB))
cout«"Об'єкти ObjA і ObjB мають однаковий тип" « endl; if(typeid(ObjA) == typeid(ObjC)) cout«"Помилка"« endl;
else
cout«"Об'єкти ObjA і ObjC мають різні типи" « endl; getchO; return 0;
}
Внаслідок виконання ця програма відображає на екрані такі результати:
Об'єкт ObjA має тип dass myClass<int>
Об'єкт ObjB має тип dass myClass<int>
Об'єкт ObjC має тип dass myClass<double>
Об'єкти ObjA і ObjB мають однаковий тип.
Об'єкти ObjA і ObjC мають різні типи.
Як бачите, незважаючи на те, що два об'єкти є примірниками одного і того ж шаблонного класу, якщо їх дані, що параметризуються, не збігаються, то вони не є еквівалентними за типом. У цьому коді програми об'єкт ObjA має тип myClass<int>, а об'єкт ObjC - тип myClass<double>. Таким чином, це об'єкти різного типу.
Розглянемо ще один приклад застосування оператора typeid до шаблонних класів, а саме модифіковану версію програми визначення геометричних фігур з попереднього підрозділу. Цього разу клас figure ми зробили шаблонним. Прозора структура коду програми дасть змогу читачу без проблем розібратися з основним принципом її роботи.
Код програми 10.6. Демонстрація механізму застосування оператора typeid до шаблонної версії figure-ієрархії класів #include <iostream> // Для потокового введення-виведення
#include <cstdlib> // Для використання бібліотечних функцій
using namespace std; // Використання стандартного простору імен
template <class myClass> class figure { protected:
myClass x, y; public:
figure(myClass _x, myClass _y) {x = _x; у = _y;} virtual myClass areaO = 0;
};
template <class myClass> class triangle: public figure<myClass> { public:
triangle(myClass _x, myClass _y): figure<myClass>(_x, _y) {} myClass areaO {return x * 0.5 * y;}
};
template <class myClass> class rectangle: public figure<myClass> { public:
rectangle(myClass _x, myClass _y): figure<myClass>(_x, _y) {} myClass areaO {return x * y;}
};
template <class myClass> class circle: public figure<myClass> { public:
circle(myClass _x, myClass _y=0): figure<myClass>(_x, _y) {} myClass areaO {return 3.14 * x * x;}
};
//Генератор об'єктів, що утворюється з класу figure. figure<double> *generator()
{
switch(rand() %3) {
case 0: return new circle<double>(10.0); case 1: return new triangle<double>(10.1,5.3); case 2: return new rectangle<double>(4.3,5.7);
}
return;
}
int mainO
{
figure<double> *p; int t = 0, c = 0, r = 0;
// Генеруємо і підраховуємо об'єкти for(int і=0; і<10; І++) {
р = generatorO;
cout«"Об'єкт має тип"«typeid(*p).nameO; cout«
// Враховуємо об'єкт
if(typeid(*p) == typeid(triangle<double>)) t++; if(typeid(*p) == typeid(rectangle<double>)) r++; if(typeid(*p) == typeid(circle<double>)) c++;
cout«"Площа дорівнює" « p->area() « endl;
}
cout« endl;
cout«"Згенеровано такі об'єкти:"« endl; cout«" трикутників:" «t« endl; cout«" прямокутників:" «r« endl; cout«" кругів:"« c « endl;
getchO; return 0;
}
Внаслідок виконання ця програма відображає на екрані такі результати::
Об'єкт має тип dass rectangle<double>. Площа дорівнює 24.51 Об'єкт має тип dass rectangle<double>. Площа дорівнює 24.51 Об'єкт має тип dass triangle<double>. Площа дорівнює 26.765 Об'єкт має тип dass triangle<double>. Площа дорівнює 26.765 Об'єкт має тип dass rectangle<double>. Площа дорівнює 24.51 Об'єкт має тип dass triangle<double>. Площа дорівнює 26.765 Об'єкт має тип dass cirde<double>. Площа дорівнює 314 Об'єкт має тип dass cirde<double>. Площа дорівнює 314 Об'єкт має тип dass triangle<double>. Площа дорівнює 26.765 Об'єкт має тип dass rectangle<double>. Площа дорівнює 24.51
Згенеровано такі об'єкти: трикутників: 4 прямокутників: 4 кругів: 2
Динамічна ідентифікація типів використовується не у кожній програмі. Але під час роботи з поліморфними типами цей засіб дає змогу дізнатися про тип об'єкта, який обробляється в будь-який довільний момент часу.
Поняття про оператори приведення типів
У мові програмування С++ визначено п'ять операторів приведення типів. Перший оператор (його було описано вище у цьому навчальному посібнику), вживається у звичайному (традиційному) стилі, був із самого початку вбудований у мові С++. Інші чотири ^упатіс_саз(, сопзісазі, геіпіегргеїсазі і зІаНс сазІ) були додані у мову всього декілька років тому. Ці оператори надають додаткові "важелі керування" характером виконання операцій приведення типу. Розглянемо кожний з них зокрема.
Оператор приведення поліморфних типів dynamic_cast
Можливо, найважливішим з нових операторів є оператор динамічного приведення типів dynamic_cast. У процесі виконання програми він перевіряє обгрунтованість пропонованої операції. Якщо у момент його виклику задана операція виявляється неприпустимою, то приведення типів не здійснюється. Загальний формат застосування оператора dynamic_cast є таким: dynamic_cast<fype> (expt)
У цьому записі елемент type означає новий тип, який є метою виконання цієї операції, а елемент ехрг- вираз, який приводиться до цього нового типу. Тип type має бути представлений покажчиком або посиланням, а вираз ехрг повинен приводитися до покажчика або посилання. Таким чином, оператор dynamic_cast можна використовувати для перетворення покажчика одного типу у покажчик іншого або посилання одного типу у посилання іншого.
Цей оператор в основному використовують для динамічного виконання операцій приведення типу серед поліморфних типів. Наприклад, якщо задано поліморфні класи В і D, причому клас D виведений з класу В, то за допомогою оператора dynamic_cast завжди можна перетворити покажчик D* у покажчик В*, оскільки покажчик на базовий клас завжди можна використовувати для вказівки на об'єкт класу, виведеного з базового. Проте оператор dynamic_cast може перетворити покажчик В* у покажчик D* тільки у тому випадку, якщо адресованим об'єктом дійсно є об'єкт класу D. І, взагалі, оператор dynamic_cast буде успішно виконаний тільки за умови, якщо дозволено поліморфне приведення типів, тобто якщо покажчик (або посилання), що приводиться до нового типу, може вказувати (або посилатися) на об'єкт цього нового типу або об'єкт, який виведено з нього. Інакше, тобто якщо задану операцію приведення типів виконати не можна, то результат дії оператора dynamic_cast оцінюється як нульовий, якщо у цій операції беруть участь покажчики1.
Розглянемо простий приклад. Припустимо, що клас Base поліморфний, а клас Derived виведений з класу Base.
Base*bp, ObjB;
Derived *dp, ObjD;
bp = &0bjD; // Присвоєння покажчику адреси об'єкта похідного класу
// Покажчик на базовий клас вказує на об'єкт класу Derived, dp = dynamic_cast<Derived *> (bp); // Приведення до покажчика на похідний клас дозволено. if(dp) cout«"Приведення типу відбулося успішно!";
Тут приведення покажчика Ьр (на базовий клас) до покажчика dp (на похідний клас) успішно здійснюється, оскільки Ьр дійсно вказує на об'єкт класу Derived. Тому у процесі виконання цього фрагмента програми буде виведено повідомлення: Приведення типу відбулося успішно!
Але наведений нижче фрагмент коду програми демонструє невдалу спробу зробити операцію приведення типу, оскільки Ьр насправді вказує на об'єкт класу Base, і неправомірно приводити покажчик на базовий клас до типу покажчика на похідний, якщо адресований ним об'єкт не є насправді об'єктом похідного класу, bp = &ObjB; // Присвоєння покажчику адреси об'єкта базового класу
// Покажчик на базовий клас посилається на об'єкт класу Base, dp = dynamic_cast<Derived *> (bp); // Помилка! if(!dp) cout«"Приведення типу виконати не вдалося";
Оскільки спроба виконати операцію приведення типу виявилася невдалою, то у процесі виконання цього фрагмента програми буде виведено повідомлення: Приведення типу виконати не вдалося.
У наведеному нижче коді програми демонструються різні ситуації застосування оператора dynamic_cast.
Код програми 10.7. Демонстрація різних ситуацій застосування використання оператора dynamic_cast #include <iostream> // Для потокового введення-виведення
using namespace std; // Використання стандартного простору імен
class baseClass { // Оголошення базового класу public: