Вызов члена производного класса из виртуальной функции

Я немного запутался в отношении виртуальных функций.

Предположим, у вас есть базовый класс с виртуальной функцией foo (), и эта функция затем переопределена в производном классе

   class Baseclass
   {  
   public:
       virtual void foo()
       {
           //...
       }
   };

   class Derived: public BaseClass
   {
   private:
       int member_val;
   public:
       Derived( int init )
           : member_val( init )
       {}
       void foo()
       {
           member_val++;
       }
   };

и foo, используя значение члена класса Derived, когда я пишу этот код

Derived d( 10 );
Base* bPtr = &d;
bPtr->foo(); 

foo () вызвал производный класс, потому что _vptr указывает на «виртуальную таблицу производного класса», а указатель в «виртуальной таблице производного класса» указывает на foo () из производного класса, но как тогда он нашел member_val, потому что базовый указатель не знает об этом. То, что «this» передается в foo () из производного класса. Мы называем его Base * (это базовый тип), но чтобы найти member_val, нам нужен Derived * (этот производный тип). Так как же это работает под капотом?


person Vahagn Babajanyan    schedule 12.02.2013    source источник


Ответы (1)


Правильный вопрос - это половина ответа. Как и в вашем случае, вы спросили, что this передается Derived::foo(), и ответ "такой же". Вы создаете объект класса Derived. Он распределяется в памяти следующим образом:

Derived:
--------
vptr_t* vptr
int member_val

Затем вы разыгрываете &d, то есть Derived*, на Base*. Что случилось с указателем? Ничего такого. Он просто изменил свой тип, а не фактический адрес, на который он указывает. Затем вы можете преобразовать его обратно в Derived*, используя dynamic_cast<Derived*>() или даже static_cast<Derived*>. В этом случае использовать и то и другое абсолютно безопасно.

Так что единственный актуальный вопрос - как работает таблица виртуальных функций? Но это другой вопрос, и вроде вы его понимаете.

Обратите внимание: сложнее обстоит дело с множественным наследованием, когда несколько базовых классов располагаются в памяти один за другим.

person Mikhail    schedule 20.06.2013
comment
Преобразование указателя может изменить его значение. В большинстве случаев, если не участвует MI, это не так, но формальной гарантии нет. - person curiousguy; 22.06.2013