- •Що таке композиція?
- •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
- •Обмеження віртуального спадкоємства
- •Коли множинне спадкоємство може бути корисним
- •Переваги використання спадкоємства
- •Спадкоємство і питання проектування
Віртуальні методи
Метод класу може бути оголошений віртуальним, якщо допускається його альтернативна реалізація в породженому класі
При виклику віртуальної функції через покажчик або заслання на об'єкт базового класу буде викликана реалізація даної функції, специфічна для фактичного типа об'єкту
Віртуальні функції позначаються в оголошенні класу за допомогою ключового слова virtual
Віртуальні функції дозволяють використовувати поліморфізм
Поліморфізм дозволяє здійснювати роботу з різними реалізаціями через один і той же інтерфейс
class CShape
{
public:
virtual std::string GetType()const{return "Shape";} virtual double GetArea()const{return 0;}
};
class CRectangle : public CShape
{
public:
CRectangle(double width, double height) :m_width(width), m_height(height){}
virtual std::string GetType()const{return "Rectangle";} virtual double GetArea()const{ return m_width * m_height; }
private:
double m_width; double m_height;
};
class CCircle : public CShape
{
public:
CCircle(double radius):m_radius(radius){}
virtual std::string GetType()const{return "Circle";}
virtual double GetArea()const{return 3.14159265 * m_radius * m_radius;} private:
double m_radius;
};
void PrintShapeArea(CShape const& shape)
{
std::cout << shape.GetType() << " area: " << shape.GetArea() << "\n";
}
int main(int argc, char * argv[])
{
CCircle circle(10);
CRectangle rectangle(20, 10);
PrintShapeArea(circle);
PrintShapeArea(rectangle);
return 0;
}
Output:
Circle area: 314.159
Rectangle area: 200
Особливості реалізації віртуальних функцій в
C++
У C++ функції, оголошені в базовому класі віртуальними, залишаються віртуальними в класах-нащадках
Використовувати слово virtual в класах спадкоємцях не обов'язково (хоча і бажано)
У C++ віртуальні функції не є віртуальними, якщо вони викликані в конструкторі або деструкції даного класу
Така поведінка специфічна для механізму ініціалізації і руйнування об'єктів в C++; у інших мовах програмування може бути по- іншому
Віртуальний
деструктор
Деструкція класу, що має спадкоємців, завжди повинна явно оголошуватися віртуальним
Це забезпечує коректний виклик деструкції потрібного класу при виклику оператора delete з покажчиком на базовий клас
Деструкція, не оголошена явно віртуальним, а також деструкція, що автоматично згенерувала, є не віртуальною
Класи без віртуальних деструкцій не призначені для розширення
Класи стандартних колекцій STL (рядки, вектори) не мають віртуальних деструкцій, тому успадковуватися від них не можна
class CBase
{
public:
CBase():m_pBaseData(new char [100])
{
std::cout << "Base class data were created\n";
}
~CBase()
{
delete [] m_pBaseData;
std::cout << "Base class data were deleted\n";
}
private:
char * m_pBaseData;
};
class CDerived : public CBase
{
public:
CDerived():m_pDerivedData(new char [1000])
{
std::cout << "Derived class data were created\n";
}
~CDerived()
{
delete [] m_pDerivedData;
std::cout << "Derived class data were deleted\n";
}
private:
char * m_pDerivedData;
};
int main(int argc, char * argv[])
{
{
CDerived derived;
}
std::cout << "===\n";
CDerived * pDerived = new CDerived();
// этот объект удалится нормально
delete pDerived; pDerived = NULL;
std::cout << "===\n";
CBase * pBase = new CDerived();
/* а вот тут будет вызван лишь деструктор базового класса */
delete pBase; pBase = NULL;
return 0;
}
Output:
Base class data were created
Derived class data were created Derived class data were deleted
Base class data were deleted
===
Base class data were created Derived class data were created Derived class data were deleted
Base class data were deleted
===
Base class data were created Derived class data were created Base class data were deleted
class CBase
{
public:
CBase():m_pBaseData(new char [100])
{
std::cout << "Base class data were created\n";
}
virtual ~CBase()
{
delete [] m_pBaseData;
std::cout << "Base class data were deleted\n";
}
private:
char * m_pBaseData;
};
class CDerived : public CBase
{
public:
CDerived():m_pDerivedData(new char [1000])
{
std::cout << "Derived class data were created\n";
}
~CDerived()
{
delete [] m_pDerivedData;
std::cout << "Derived class data were deleted\n";
}
private:
char * m_pDerivedData;
};
int main(int argc, char * argv[])
{
{
CDerived derived;
}
std::cout << "===\n";
CDerived * pDerived = new
CDerived();
// этот объект удалится нормально delete pDerived; pDerived = NULL;
std::cout << "===\n";
CBase * pBase = new CDerived();
/* а вот тут будет вызван лишь деструктор базового класса */
delete pBase; pBase = NULL;
return 0;
}
Output:
Base class data were created
Derived class data were created Derived class data were deleted Base class data were deleted
===
Base class data were created Derived class data were created Derived class data were deleted Base class data were deleted
===
Base class data were created Derived class data were created Derived class data were deleted Base class data were deleted
Підводимо підсумки
Завжди використовуємо віртуальну деструкцію:
У базових класах
У класах, від яких можливе спадкоємство в майбутньому
Наприклад, в класах з віртуальними методами
Не використовуємо віртуальні деструкції
У класах, від яких не планується створювати похідні класи в майбутньому
Також можливо в базовому класі оголосити захищену невіртуальну деструкцію
Об'єкти даного видалити безпосередньо неможливо - лише через покажчик на клас-спадкоємець
Дана деструкція буде доступна класам-спадкоємцям
Абстрактні класи
Можливі ситуації, коли базовий клас є
абстрактним поняттям, і виступає лише як базовий клас (інтерфейс) для похідних класів
Неможливо дати осмислене визначення його віртуальних функцій
Яка площа об'єкту «CShape», як його намалювати?
Такі віртуальні функції слід оголошувати чисто віртуальними (pure virtual), додавши ініціалізатор =0, опустивши тіло функції
Клас є абстрактним, якщо в нім міститься хоч би одна чисто віртуальна функція, або він не реалізує хоч би одну чисто віртуальну функцію свого батька
Екземпляр абстрактного класу створити
неможливо