- •Полиморфизм
- •Способность объектно-ориентированных языков автоматически определять тип объекта
- •Виртуальные функции имеет смысл использовать только для классов, являющихся базовыми.
- •Правило: указатель на базовый класс может ссылаться на объект этого класса или любого
- •Пример. Использование указателей на классы.
- •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 Dog: public Animal
{
public:
Dog(char *name):Animal(name) {} char *speak() {
char *phrase; phrase=strdup(pname); return strcat(phrase," говорит Гав! ");
}
virtual char *sit() {
char *phrase; phrase=strdup(pname); return strcat(phrase," сидит");
}
char *eat() {
char *phrase; phrase=strdup(pname);
return strcat(phrase," ест всё!!! ");
}
};
int main() {
//Animal p3("Дружок"); //Ошибка!!! 'Animal': cannot instantiate abstract class
Dog druzhok("Дружок"); printf("%s\n",druzhok.speak()); printf("%s\n",druzhok.eat()); return 0;
}
Виртуальные деструкторы class Base1
{
public:
~Base1(){
printf("~Base1()\n");}
};
class Derived1: public Base1
{
public:
~Derived1(){
printf("~Direved1()\n");}
};
class Base2
{
public:
virtual ~Base2(){ printf("~Base2()\n");}
};
class Derived2: public Base2
{
public:
~Derived2(){
printf("~Direved2()\n");}
};
int main()
{
Base1* bp1=new Derived1;
delete bp1;
Base2* bp2=new Derived2; delete bp2;
Derived1* bp3=new Derived1;
delete bp3;
Derived2* bp4=new Derived2; delete bp4;
return 0;
}
Чистые виртуальные деструкторы
В отличие от чистых виртуальных функций для чистых виртуальных деструкторов необходимо обеспечить тело функции.
При реализации деструкторов производных классов их тело задавать не нужно.
class AbstractBase
{
public:
virtual ~AbstractBase()=0;
};
AbstractBase::~AbstractBase() { printf("Destructor Abstract Class\n");
}
class Derived: public AbstractBase
{
};
int main()
{
Derived d; return 0;
}
Виртуальные функции в деструкторах
Механизм позднего связывания не работает для виртуальных и невиртуальных деструкторов.
То есть, если в теле деструктора вызывается виртуальная функция, то вызывается её локальная версия.
Пример. Вызов виртуальной функции из тела деструктора.
class Base
{
public:
virtual void f() {printf("Base::f\n");}
virtual ~Base() {
printf("~Base()\n");
f();
}
};
class Derived: public Base { public:
~Derived() { printf("~Derived()\n");
}
virtual void f() { printf("Derived::f\n");
}
};
int main {
Base *bp = new Derived; delete bp;
return 0;
};
Перегруженные функции при динамическом связывании
Общее правило: перегруженная
переопределенная функция базового класса в производных классах прячет все другие версии этой функции в базовом классе.