- •1 Парадигми та мови програмування
- •1.1 Процедурне програмування
- •1.2 Об'єктне (модульне) програмування
- •1.3 Об'єктно-орієнтовне програмування
- •2 Програмне середовище
- •3 Базові поняття програмування
- •4 Процедурно-орієнтоване програмування
- •4.1 Функції
- •4.2 Вбудовані (inline) функції
- •4.3 Передача параметрів
- •4.4 Обчислення значення функцій
- •4.4. Обчислення значення функції — вихід із функції; особливості повернення та використання іменованого значення, іменованої константи
- •4.5 Рекурсія
- •4.6 Довизначення (overloading) функцій
- •4.6. Довизначення (overloading) функцій
- •4.7 Узагальнені функції (function template)
- •4.8 Непряме використання функцій: указники на функції, їх тип, ініціалізація і присвоєння
- •4.9 Видимість
- •4.10 Тривалість життя об’єктів
- •5 Об'єктне програмування
- •5.1 Класи і об'єкти, члени класів
- •5.2 Інтерфейс класу, реалізація класу; визначення і оголошення класу
- •5.3 Створення і ініціалізація об'єктів, довизначення конструкторів, замовчуваний конструктор, копіювання, поверхневе і глибоке копіювання, ініціалізація і присвоєння, копіювальний конструктор
- •5.4 Cтатичні члени класів і статичні класні функції
- •5.5 Константні об'єкти, константні функції, змінювані члени константних об'єктів (mutable)
- •5.6 Поточний об'єкт this, указники на члени класу і класні функції, порівняння з указниками на (позакласні) функції, указники на статичні члени класу
- •5.7 5.8. Довизначення (overloading) операцій
- •5.7 5.8. Довизначення (overloading) операцій
- •5.7 5.8. Довизначення (overloading) операцій
- •5.7 5.8. Довизначення (overloading) операцій
- •5.7 5.8. Довизначення (overloading) операцій
- •5.7 5.8. Довизначення (overloading) операцій
- •5.8 Клас string: конструктори і присвоєння рядків, операції, відкладене копіювання
- •5.9 Параметризовані класи (class template): визначення шаблону, конкретизація
- •6 Об'єктно-орієнтовне програмування
- •6.1 Ієрархія об’єктів і ієрархія класів
- •6.2 Відкрите, закрите і захищене успадкування, успадкування типу і успадкування реалізації; абстрактні класи
- •6.3 Створення похідних об'єктів
- •6.4 Статичне і динамічне зв’язування
- •6.5 Успадкування із спільного базового класу. Кратне (multiple) успадкування. Віртуальне успадкування, порядок виклику конструкторів
- •7 Екзаменаційні завдання
- •7 Екзаменаційні завдання
6.3 Створення похідних об'єктів
Створення похідних об’єктів, конструктор похідного класу, послідовність виклику конструкторів, ліквідація похідних об’єктів, деструктор похідного класу, послідовність виклику деструкторів
Наступний приклад дає вичерпну інформацію про порядок створення і ліквідації
об'єктів суперкласів і підкласів. Схема успадкувань зображена на малюнку.
// Хижак
class Predator
{
private:
string favoritePrey;
public:
Predator(const string& prey);
~Predator();
};
Predator::Predator(const string& prey):favoritePrey(prey)
{
cout << "Favorite prey: "
<< prey << "\n";
}
Predator::~Predator()
{
cout << "Predator destructor was called!\n\n";
}
|
|
// Пестун class Pet { private: string favoriteToy; public: Pet(const string& toy ); ~Pet(); }; Pet::Pet(const string& toy ):favoriteToy(toy) { cout << "Favorite toy: " << toy << "\n"; } Pet::~Pet() { cout << "Pet destructor was called!\n"; } // Кішка: Хижак, Пестун class Cat : public Predator, public Pet { private: short catID; static short lastCatID; public: Cat(const string& prey, const string& toy ); ~Cat(); static void ShowCurrentID(); }; Cat::Cat( const string& prey, const string& toy ) : Predator( prey ), Pet( toy ) { catID = ++lastCatID; cout << "catID: " << catID << "\n---------\n"; } Cat::~Cat() { cout << "Cat destructor called: catID = " << catID << "...\n"; } void Cat:: ShowCurrentID() { cout << "catID: " <<lastCatID <<"\n---------\n"; } short Cat::lastCatID = 0;
int main() { Cat TC( "Mice", "Ball of yarn" ); Cat:: ShowCurrentID() Cat Benny( "Crickets", "Bottle cap" ); Cat:: ShowCurrentID() Cat Meow( "Moths", "Spool of thread" ); Cat:: ShowCurrentID() return 0; } Favorite prey: Mice Favorite toy: Ball of yarn catID: 1 --------- Favorite prey: Crickets Favorite toy: Bottle cap catID: 2 --------- Favorite prey: Moths Favorite toy: Spool of thread catID: 3 --------- Cat destructor called: catID = 3... Pet destructor was called! Predator destructor was called!
Cat destructor called: catID = 2... Pet destructor was called! Predator destructor was called!
Cat destructor called: catID = 1... Pet destructor was called! Predator destructor was called!
6.4 Статичне і динамічне зв’язування
Статичне і динамічне зв’язування: поліморфізм, віртуальні функції, заміщення (overriding) функцій, віртуальні деструктори
Успадкування надає можливість не тільки довизначити метод, залишений не визначеним
у батьківському класі, але й замістити його іншим ( заміщення — overridding ).
Причому рішення про те, яку саме функцію буде викликано приймається на етапі
виконання програми залежно від конкретного типу створеного об'єкту. Це явище,
при якому об'єкти одного типу (підкласи) заміщують об'єкти іншого типу (суперкласу)
дістало назву поліморфізму.
Наступний приклад показує просте застосування поліморфізму
|
|
Заміщувані функції, як і абстрактні, позначаються як віртуальні .
Це свого роду невеликий пережиток мови С++, оскільки в пізніших мовах необхідність
виділення віртуальних функцій відпала — замістити можна будь-яку функцію.
class Shape
{
public:
virtual void whatAmI();
};
void Shape::whatAmI()
{
cout <<"I don't know what kind of shape I am!\n";
}
class Rectangle : public Shape
{
public:
void whatAmI();
};
void Rectangle::whatAmI()
{
cout << "I'm a rectangle!\n";
}
class Triangle : public Shape
{
public:
void whatAmI();
};
void Triangle::whatAmI()
{
cout << "I'm a triangle!\n";
}
Зверніть увагу, в усіх трьох випадках виклику методу whatAmI використовується
указник одного й того ж типу, але викликаються різні методи, залежно від типу
значення, на яке указник встановлено. Ясно, що використання імен замість указників,
зробило б поліморфізм неможливим.
int main()
{
Shape *s1, *s2, *s3;
s1 = new Rectangle;
s2 = new Triangle;
s3 = new Shape;
s1->whatAmI();
s2->whatAmI(); (*)
s3->whatAmI();
return 0;
}
Особливу роль відіграють віртуальні деструктори . Вони призначені
для виклику правильних деструкторів у випадку поліморфізму. Ось вже відомий
нам приклад. Якби деструктор не був віртуальним, то після створенні прямокутника
викликався б лише деструктор фігури, а тому пам'ять, виділена під прямокутник
залишилася б не звільненою.
const int sizeOfShape=100;
const int sizeOfRec=60;
class Shape
{
public:
double *description;
Shape()
{
cout<<"Shape constructor was called"<<endl;
description = new double[sizeOfShape];
}
virtual void whatAmI();
virtual ~Shape()
{
delete [] description;
cout<<"Shape destructor was called"<<endl;
}
};
void Shape::whatAmI()
{
cout << "I don't know what kind of shape I am!\n";
}
class Rectangle : public Shape
{
public:
double *description;
Rectangle()
{
cout<<"Rectangle constructor was called"<<endl;
description = new double[sizeOfRec];
}
virtual void whatAmI();
~Rectangle()
{
delete [] description;
cout<<"Rectangle destructor was called"<<endl;
}
};
void Rectangle::whatAmI()
{
cout << "I'm a rectangle!\n";
}
int main()
{
Shape *s1, *s2;
s1 = new Shape;
s1->whatAmI();
delete s1;
cout<<endl;
s2 = new Rectangle;
s2->whatAmI();
delete s2;
return 0;
}
Останній приклад показує спосіб, яким можна звичайний масив замінити масивом
з контролем за виходом індексу за межі масиву. Операція індексування оголошується
віртуальною
class IntArray
{
public:
. . . . . . .
IntArray& operator= (const IntArray&);
virtual int& opetator[] (int index);
int size() const;
. . . . . . .
}
class IntArrayRC: public IntArray
{
public:
IntArrayRC (int sz=defaultArraySize):IntArray(sz){};
IntArrayRC (int* array, int sz):
IntArray(array, sz){};
IntArrayRC (const IntArray&):IntArray(r){};
int& operator[] (int index);
private:
void checkRange(int index);
}
int& IntArrayRC ::operator[] (int index)
{
checkRange(index);
return ia[index];
}
#include
void IntArrayRC:: checkRange(int index)
{
assert (index>=0 && index <_size);
}
Ще ближче до повного рішення:
template
class ArrayRC: public Array
{
public:
ArrayRC (int sz=defaultArraySize):
Array(sz){};
ArrayRC (int* array, int sz):
Array(array, sz){};
ArrayRC (const IntArray&):Array(r){};
elemType& operator[] (int index);
private:
void checkRange(int index);
}
Якщо кожен з базових класів випадково чи зумисне міститиме елементи з одним і
тим же іменем, то похідний клас успадкує обидва з них. Для використання таких
елементів може знадобитись уточнення, наприклад, Predator::name .
|
|
class Predator
{
private:
string favoritePrey;
protected:
string name;
public:
Predator(string aName, string aPrey );
~Predator();
setName(const string& aName);
};
class Pet
{
private:
string favoriteToy;
protected:
string name;
public:
Pet(string aName, string aToy );
~Pet();
setName(const string& aName);
};
class Cat : public Predator, public Pet
{
private:
short catID;
static short lastCatID;
public:
Cat(string aName, string aPrey, string aToy );
~Cat();
static void ShowLastID();
};
Не дуже добре:
Cat myCat(“Murka”, “Mouse” “Mouse”);
//Як Вас тепер називати?
Pet::MyCat.setName(”NotMurka”);
Predator::MyCat.setName(”StillMurka”);
Було б зручно мати об'єкт на зразок “іменованого звіра”, а вже від нього виводити
хижака і пестуна. Але це вже проблема успадкування із спільного базового класу.
