C++ 3d.Комментарии




Виртуальные функции


Стоит помнить, что традиционной и очевидной реализацией вызова виртуальной функции является просто косвенный вызов функции...

Это, вообще говоря, неверно. При применении множественного наследования "просто косвенного вызова" оказывается недостаточно. Рассмотрим следующую программу: #include <stdio.h>

struct B1 { int b1; // непустая virtual ~B1() { } };

struct B2 { int b2; // непустая virtual void vfun() { } };

struct D : B1, B2 { // множественное наследование от непустых классов virtual void vfun() { printf("D::vfun(): this=%p\n", this); } };

int main() { D d;

D* dptr=&d; printf("dptr\t%p\n", dptr); dptr->vfun();

B2* b2ptr=&d; printf("b2ptr\t%p\n", b2ptr); b2ptr->vfun(); }

На своей машине я получил следующие результаты: dptr 0x283fee8 D::vfun(): this=0x283fee8 b2ptr 0x283feec

D::vfun(): this=0x283fee8

Т.е. при вызове через указатель на производный класс dptr, внутри D::vfun() мы получим this=0x283fee8. Но несмотря на то, что после преобразования исходного указателя в указатель на (второй) базовый класс b2ptr, его значение (очевидно) изменилось, внутри D::vfun() мы все равно видим исходное значение, что полностью соответствует ожиданиям D::vfun() относительно типа и значения своего this.

Что же все это означает? А означает это то, что если бы вызов виртуальной функции struct D : B1, B2 { virtual void vfun(D *const this) // псевдокод { // ... } };

через указатель ptr->vfun() всегда сводился бы к вызову (*vtbl[index_of_vfun])(ptr), то в нашей программе мы бы получили b2ptr==0x283feec==this!=0x283fee8.

Вопрос номер два: как они это делают? Суть проблемы в том, что одна и та же замещенная виртуальная функция (D::vfun() в нашем случае) может быть вызвана как через указатель на производный класс (ptr==0x283fee8) так и через указатель на один из базовых классов (ptr==0x283feec), чьи значения не совпадают, в то время как переданное значение this должно быть одним и тем же (this==0x283fee8) в обоих случаях.




Содержание  Назад  Вперед