- •Полиморфизм
- •Способность объектно-ориентированных языков автоматически определять тип объекта
- •Виртуальные функции имеет смысл использовать только для классов, являющихся базовыми.
- •Правило: указатель на базовый класс может ссылаться на объект этого класса или любого
- •Пример. Использование указателей на классы.
- •int main(){
- •Пример. Использование виртуальных функций.
- •void sound(Animal& i)
- •Переопределим класс Animal:
- •Пример. Использование указателей на объекты. Приведение типов.
- •class Dog: public Animal
- •int main()
- •Механизм работы позднего связывания
- •В каждом классе также помещается указатель VFPTR на таблицу виртуальный функций.
- •При явном связывании мы получаем
- •Для указателей, имеющих тип базового класса, компилятор запрещает вызовы тех виртуальных функций, которые
- •Абстрактные базовые классы
- •Пример. Работа с абстрактным классом
- •Чистые виртуальные функции могут быть определены, но только вне объявления класса.
- •class Dog: public Animal
- •char *eat() {
- •Виртуальные деструкторы class Base1
- •class Base2
- •int main()
- •Чистые виртуальные деструкторы
- •Виртуальные функции в деструкторах
- •int main {
- •Пример. Перегруженные переопределённые функции
- •class Derived2: public Base { public:
- •int main() { char *s;
- •Множественное наследование
- •Вызов конструкторов при создании объекта класса D происходит в том порядке, в котором
- •class A { public:
- •class C: public A, public B { public:
- •Виртуальный базовый класс
- •class A:public virtual W { public:
- •int main() {
- •Виртуальные базовые классы инициализируются перед любыми невиртуальными базовыми классами в том порядке, в
- •Пример. Использование одного класса в качестве виртуального и невиртуального базового класса
Пример. Перегруженные переопределённые функции
class Base { public:
virtual int f() {printf("Base::f()\n");return 1;} virtual void f(char *s) {printf("%s\n",s);}
virtual void g() {};
};
class Derived1: public Base { public:
void g() { printf("Derived1::g()\n");}
};
class Derived2: public Base { public:
int f() { printf("Derived2::f()\n"); return 2;
}
};
/*class Derived3: public Base {public:
void f() {printf("Derived3::f()\n");} //Ошибка!!! Компилятор не позволяет изменять тип возвращаемого значения для перегруженной виртуальной функции };*/
int main() { char *s;
s="Hello!";
Derived1 d1;
int x=d1.f(); //x=1 d1.f(s); Derived2 d2; x=d2.f(); //x=2
//d2.f(s); //ошибка! функция f не имеет ни
одного параметра
Base* b2=&d2; b2->f(); b2->f(s);
return 0;
}
Множественное наследование
Множественное наследование описывает родство между классами, при котором один класс может иметь несколько базовых.
class A {…}; class B {…}; class C {…};
class D: public A, public B, public C {…};
A |
|
B |
|
C |
|
|
|
|
|
D
Вызов конструкторов при создании объекта класса D происходит в том порядке, в котором были объявлены базовые классы:
A::A()
B::B()
C::C()
D::D()
Вызов перегруженных функции при множественном наследовании
Базовый |
Базовый |
класс A |
класс B |
Display() |
Display() |
|
|
|
C |
class A { public:
void Display(void) { printf("A::Display()\n");
}
};
class B { public:
void Display(void) { printf("B::Display()\n");
}
};
class C: public A, public B { public:
void f();
};
void C::f()
{ //Display();//'Display': identifier not found A::Display();
B::Display();
}
int main() { C c; c.f(); return 0;
}
Виртуальный базовый класс
W
A/W |
|
B/W |
|
|
|
C/A,B
Если через объект класса C попытаться обратиться к методам класса W, то компилятор выдаст ошибку.
Чтобы решить эту задачу используется виртуальный базовый класс.
W (f, g, h)
A (g) |
|
B (f) |
|
|
|
C (f, g, h)
class W { public:
virtual void f() {printf("W::f()\n");} virtual void g() {printf("W::g()\n");} virtual void h() {printf("W::h()\n");}
};
class A:public virtual W { public:
void g() {printf("A::g()\n");}
};
class B:public virtual W { public:
void f() {printf("B::F()\n");}
};
class C: public A, public B { public:
void f() {printf("C::f()\n");}
};