- •Що таке композиція?
- •class CPoint
- •// Колесо
- •// Слайд class CSlide
- •Що таке
- •Термінологія
- •Тварина
- •Варіанти
- •Публічне (відкрите) спадкоємство
- •class CPerson
- •Публічне спадкоємство як
- •CShape
- •CPoint
- •Приватне (закрите) спадкоємство
- •class CIntArray
- •Композиція - переважна
- •class CIntArray
- •Захищене
- •class CIntArray
- •Відмінності між захищеним і відкритим
- •Типи спадкоємства в інших мовах
- •Порядок виклику конструкторів
- •Конструктор класу CEmployee (службовець) оголошений захищеним, аби не допустити безглузде створення абстрактних «службовців»
- •Порядок виклику деструкторів
- •class CTable
- •Перевантаження
- •class CBase
- •Завдання - ієрархія геометричних фігур
- •class CShape
- •int main(int argc, char * argv[])
- •void PrintShapeArea(CShape const& shape)
- •У чому ж проблема?
- •Віртуальні методи
- •class CShape
- •void PrintShapeArea(CShape const& shape)
- •Особливості реалізації віртуальних функцій в
- •Віртуальний
- •class CBase
- •class CBase
- •Підводимо підсумки
- •Абстрактні класи
- •class CShape
- •Інтерфейс
- •class IShape
- •Приведення типів
- •Оператор
- •class CAnimal
- •CMammal const& MakeMammal(CAnimal const & animal)
- •Не зловживайте
- •class CAnimal
- •Множинне
- •IDrawable
- •// інтерфейс об'єктів, які можна намалювати
- •Проблеми, що виникають при множинному спадкоємстві
- •CAnimal
- •//Животное class CAnimal
- •Можливе вирішення даної проблеми - віртуальне спадкоємство
- •//Тварина class CAnimal
- •Обмеження віртуального спадкоємства
- •Коли множинне спадкоємство може бути корисним
- •Переваги використання спадкоємства
- •Спадкоємство і питання проектування
class CShape
{
public:
virtual std::string GetType()const=0; virtual double GetArea()const=0; virtual void Draw()const=0;
};
Інтерфейс
Неможливо створити екземпляр абстрактного класу
Всі методи абстрактного класу мають бути реалізовані в похідних класах
Абстрактний клас, що містить лише чисто віртуальні методи ще називають
інтерфейсом
Деструкція такого класу обов'язково має бути віртуальною (не обов'язково чисто віртуальним)
У деяких ОО мовах програмування для оголошення інтерфейсів можуть існувати окремі конструкції мови
Ключове слово слово interface в Java/C#/ActionScript
class IShape
{
public:
virtual void Transform()=0; virtual double GetArea()const=0; virtual void Draw()const=0; virtual ~IShape(){}
};
class CRectangle : public IShape
{
public:
virtual void Transform()
{
...
}
virtual double GetArea()const
{
...
}
virtual void Draw()const
{
...
}
}
class CCircle : public IShape
{
public:
virtual void Transform()
{
...
}
virtual double GetArea()const
{
...
}
virtual void Draw()const
{
...
}
}
Приведення типів
в межах ієрархії
класівПриведення типів вгору за ієрархією завжди можливо і може відбуватися неявно
Всякий собака є твариною
Всякий яструб є птицею
Виключення - ромбоподібне множинне спадкоємство
Приведення типів вниз за ієрархією не завжди можливо
Не всякий ссавець - собака, але деякі ссавці можуть бути собаками
У C++ для такого приведення типів використовується оператор dynamic_cast
Приведення типа між незв'язаними класами ієрархії недопустимо
Собаки не є птицями
Кішка - не яструб і не собакаЯструб - не ссавець
Оператор
dynamic_cast
Оператор приведення типа dynamic_cast дозволяє виконати безпечне приведення заслання або покажчика на одного типа даних до іншого
Перевірка допустимості приведення типа здійснюється під час виконання програми
При неможливості приведення типа буде повернений нульовий покажчик (при приведенні типа покажчика) або згенерувало виключення типа std::bad_cast (при приведенні типа заслання)
Для здійснення перевірок часу виконання використовується інформація про типів (RTTI - Run- Time Type Information)
RTTI вимагає, аби в класі був хоч би один віртуальний метод (хоч би деструкор)
class CAnimal
{
public:
virtual ~CAnimal()
{}
};
class CBird
: public CAnimal
{};
class CEagle
: public CBird {};
class CMammal
: public CAnimal
{};
class CDog
: public CMammal
{};
class CCat
: public CMammal {};
void PrintAnimalType(CAnimal const * pAnimal)
{
if (dynamic_cast<CDog const*>(pAnimal) != NULL) std::cout << "dog\n";
else if (dynamic_cast<CCat const*>(pAnimal) != NULL) std::cout << "cat\n";
else if (dynamic_cast<CEagle const*>(pAnimal) != NULL) std::cout << "eagle\n";
else if (dynamic_cast<CMammal const*>(pAnimal) !=
NULL)
std::cout << "some unknown type of mammals\n"; else if (dynamic_cast<CBird const*>(pAnimal) != NULL)
std::cout << "some unknown type birds\n"; else
std::cout << "some unknown type of animals\n";
}
int main(int argc, char* argv[])
{
CDog dog; PrintAnimalType(&dog);
CAnimal * pAnimal = new CCat();
PrintAnimalType(pAnimal); delete pAnimal;
return 0;
}
CMammal const& MakeMammal(CAnimal const & animal)
{
return dynamic_cast<CMammal const&>(animal);
}
int main(int argc, char* argv[])
{
CDog dog;
CMammal const& dogAsMammal = MakeMammal(dog);
CCat cat;
// неявное приведение типов вверх по иерархии Cat -> Animal
CAnimal const& catAsAnimal = cat;
CMammal const& animalAsMammal = MakeMammal(catAsAnimal);
CEagle eagle; try
{
CMammal const& eagleAsMammal = MakeMammal(eagle);
}
catch(std::bad_cast const& error)
{
std::cout << error.what() << "\n";
}
return 0;
}
Не зловживайте
використанням dynamic_cast
Скрізь, де це можна, слід обходитися без використання даного оператора, віддаючи перевагу віртуальним (або чисто віртуальним функціям)
Інакше при додаванні нового класу в ієрархію може знадобитися провести ревізію всієї коди, що використовує dynamic_cast
При використанні віртуальних функцій нічого особливого робити не треба
class CAnimal
{
public:
virtual std::string GetType()const = 0; virtual ~CAnimal(){}
};
//птицы и млекопитающие – абстрактные понятия
//поэтому в них реализовывать GetType() нет
смысла
class CBird : public CAnimal{}; class CMammal : public CAnimal{};
class CEagle : public CBird |
|
{ |
class CCat : public CMammal |
public: |
{ |
virtual std::string GetType()const |
public: |
{return "eagle";} |
virtual std::string GetType()const |
}; |
{return "cat";} |
class CDog : public CMammal |
}; |
|
|
{ |
void PrintAnimalType(CAnimal const & |
public: |
animal) |
virtual std::string GetType()const |
{ |
{return "dog";} |
std::cout << animal.GetType() << "\n"; |
}; |
} |