План:
Абстрактни класове
Получаване на информация по време на изпълнение
Многократна наследственост Абстрактни класове (преговор)
Пример: Йерархия на геометрични фигури в равнината.
- Ако искаме да имаме полиморфна функция за лице на фигура - area, то тази функция трябва да бъде
дефинирана в базовия клас Shape.
- Тази функция има смисъл за всеки клас, производен на класа Shape, но в класа Shape
тя няма смисъл.
- Такава функция се декларира по специален начин в базовия клас и се
нарича чисто виртуална член-функция.
- Клас, който има поне една чисто виртуална
член-функция се нарича абстрактен клас.
- Клас, който се състои изцяло от чисто виртуални член-функции
понякога се нарича интерфейс.
Пример: Абстрактен клас Shape и производни класове
Triangle и Circle.
class Triangle: public Shape {
public:
... virtual double perimeter() const
{ return a + b + c; } virtual double area() const
{ double p = (a + b + c)/2;
return
sqrt(p*(p-a)*(p-b)*(p-c));
}
private:
double a, b, c;
};
class Circle: public Shape {
public:
... virtual double perimeter()
const
{ return 2*PI*r; } virtual double area()
const
{ return PI*r*r; }
private:
double r;
};
...
int main()
{
// Shape* s = new Shape;
// ERROR!!
Shape* s = new Triangle(4, 5,
6); // First *s is a triangle cout <<
"Area is " << s->area() << "\n";
s = new Circle(10);
//
Now *s is a circle
cout << "Area is " <<
s->area() << "\n";
...
return 0;
} abstract.cpp
* Операция dynamic_cast
Унарната операция dynamic_cast променя типа на указател.
Тя изисква тип като параметър на шаблон, и аргумент, който трябва да
бъде указател или псевдоним (референция).
Пример:Manager е производен клас на Employee
с виртуална функция: // Implicitly converts pointer e from
Manager* to Employee* Employee* e = new Manager("Sarah",
67000, "A");
// Explicitly converts pointer e from
Employee* to pointer m from Manager* Manager* m =
dynamic_cast<Manager*>(e); if (m != NULL) cout <<
m->get_name() << endl << m->get_department()
<< endl;
Ако аргументът е указател сочещ към клас, който е различен от
параметъра на шаблона, резултатът от операцията е NULL. Employee* e = new Employe("Sarah",
67000);
// Explicitly converts pointer e from
Employee* to pointer m from Manager* Manager* m =
dynamic_cast<Manager*>(e);
if (m == NULL) cout << "NULL" <<
endl;
Пример: Проверка дали указател сочи към обект от даден клас в
полиморфна колекция.
vector<Employee*> department;
.......
for (int i = 0; i < department.size(); i++) { Manager* m =
dynamic_cast<Manager*>(department[i]); if (m != NULL) {
cout << "Employee " << department[i]->get_name()
<< " is a manager.\n";
m->set_bonus(2000); // Can now invoke manager member functions } else
cout << "Employee " << department[i]->get_name()
<< " is not a
manager.\n"; } dynamic.cpp
За да се получи (името на) конкретен тип на даден
обект, се използва операция typeid.
Аргумент на операцията е израз, или име на клас и връща обект от тип
type_info, който е дефиниран в заглавния файл <typeinfo>.
В класа type_info има дефинирана член-функция name(),
която връща име на тип.
Пример: for (int i = 0; i < department.size(); i++) cout <<
typeid(*department[i]).name() << "\n";
Друг начин за тестване на типа на обект е да се сравнява стойността
в typeinfo с тази на известен клас:
for (int i = 0; i < department.size(); i++) { if
(typeid(*department[i]) == typeid(Manager))
cout << "Employee " << department[i]->get_name()
<< " is a manager. \n"; else
cout << "Employee " << department[i]->get_name()
<< " is not a manager. \n"; }
Използването на dynamic_cast и typeid
трябва да се избягва, като се замества с механизма на виртуалните
функции. Многократна наследственост (множествено
наследяване)
Пример: Потоковата библиотека на
С++ се състои от няколко класове, свързани в следната йерархия:
Когато се използва многократно наследяване, диаграмата за
наследяване на класове вече не е дърво, а насочен ацикличен граф или
DAG.
Пример:
Викане на функция в производен клас, която е дефинирана и в двата
базови класове.
// multi.cpp #include<iostream> using namespace std; class Student { public: Student() { id = "sid"; } virtual string get_id() const { return id; } private: string id; };
class TeachingAssistant : public Employee, public Student { public: TeachingAssistant():Employee(),
Student(){} // string get_id() const; string student_id() const; };
string TeachingAssistant::student_id() const // Make student value available by a different name { return Student::get_id(); }
int main() { TeachingAssistant* fred = new
TeachingAssistant(); Employee* new_hire =
fred; // Legal, because a TeachingAssistant is-a
Employee Student* advisee =
fred; // Legal, because a
TeachingAssistant is-a Student
cout << "Your number is " <<
fred->get_id() << "\n"; // Error, ambiguous
member function name Student* mary = new Student(); // Student* mary = new
TeachingAssistant();
TeachingAssistant* lab_instructor =
dynamic_cast<TeachingAssistant*>(mary); if (lab_instructor != NULL) cout <<
"Yes, mary is a TeachingAssistant. \n"; else cout <<
"No, mary is not a TeachingAssistant. \n"; return 0; }
Решение е да си използва пълното име на функцията или да се
предефинира тази функция в производния клас.
class TeachingAssistant : public Student, public Employee { public: string get_id() const; string student_id() const; }; // get_id will return Employee identification number string
TeachingAssistant::get_id() { return Employee::get_id(); } string TeachingAssistant::student_id() // Make student value available by a different name { return Student::get_id(); } Multiple
Inheritance in C++
* Дублиране на базови класове. class MultiplePartTime : public Employee, public
Employee // Error { ... };
При използване на множествено наследяване може да се появи дублиране
на базов клас.
Пример: Student и Teacher са производни
на Person, а TeachingAssistant е производен на Student
и Teacher.
class Person { public: Person(string n); string get_name()
const; private: string name; }; class Student : public Person { ... }; class Employee : public Person { ... };Cay S. Horstmann, Timothy A. Budd Big C++ 2008 class TeachingAssistant : public Employee, public Student {
... };
Във всеки обект от клас TeachingAssistant се
съдържат два пъти данни от клас Person.
За да имаме само един екземпляр от клас Person, използваме виртуално
наследяване.
class Student : virtual public Person { ... }; class Employee : virtual public Person { ... }; class TeachingAssistant : public Student, public Employee { ... };
Cay S. Horstmann, Timothy A. Budd Big C++ 2008 double.cpp