2016-03-18 7 views
2

Это похоже на многие предыдущие вопросы, но он спрашивает то, что я не смог найти.Выбор vptr в случае множественного наследования

#include <iostream> 
using namespace std; 

class Base1 { 
    public: 
     int b1_data; 
     virtual void b1_fn() {cout << "I am b1\n";} 
}; 
class Base2 { 
    public: 
     int b2_data; 
     virtual void b2_fn() {cout << "I am b2\n";} 
}; 
class Derived : public Base1, public Base2 { 
    public: 
     int d_data; 
     void b1_fn() {cout << "I am b1 of d\n";} 
     void b2_fn() {cout << "I am b2 of d\n";} 
}; 

int main() { 
    Derived *d = new Derived(); 
    Base1 *b1 = d; 
    /*My observation mentioned below is implementation dependant, for learning, 
    I assume, there is vtable for each class containing virtual function and in 
    case of multiple inheritance, there are multiple vtables based on number of 
    base classes(hence that many vptr in derived object mem layout)*/ 

    b1->b1_fn(); // invokes b1_fn of Derived because d points to 
       // start of d's memory layout and hence finds vtpr to 
       // Derived vtable for Base1(this is understood) 
    Base2 *b2 = d; 
    b2->b2_fn(); // invokes b2_fn of Derived but how? I know that it "somehow" 
       // gets the offset added to d to point to corresponding Base2 
       // type layout(which has vptr pointing to Derived vtable for 
       // Base2) present in d's memory layout. 
    return 0; 
} 

В частности, как же точки b2 в vptr для производных виртуальных таблиц для Base2, чтобы добраться до b2_fn()? Я пробовал видеть memlayout дамп из gcc, но не мог понять много.

+2

Итак, ваш вопрос: «Как GCC реализует виртуальные функции с множественным наследованием»? –

+0

Вид да, но чтобы сузить его, я хочу знать, как он выполняет привязку, когда есть несколько базовых классов, и все они не отображаются в макете объектов с одинаковым смещением. В принципе, источник этого «смещения» я ищу. –

ответ

2

Компилятор в случае множественного наследования, постройте его vtables, чтобы каждый подобъект имел соответствующую таблицу vtable. Конечно, это зависит от реализации (как самих виртуальных таблиц), но это будет организовано так:

  • Base1 объект имеет vptr, указывающий на таблицу виртуальных, содержащий уникальный указатель на Base1::b1_fn
  • Base2 объект имеет vptr указывая на виртуальные таблицы, содержащих уникальный указатель на Base2::b2_fn
  • A Derived объекта имеет vptr, указывающий на виртуальные таблицы, которая начинается с виртуальных таблиц макета, соответствующая Base1, но расширил его отсутствующими элементами таблицы Base2. С «Макет» я имею в виду, что указатель на b1_fn() имеет такое же смещение, но может указывать на функцию переопределения. Итак, здесь таблица будет содержать Derived::b1_fn, а затем Derived::b2_fn. Эта комбинированная компоновка гарантирует, что подобъект Base1 в Derived может совместно использовать vtable со своим дочерним элементом.
  • Но Derived объекта состоит из 2 подобъектов: так Base1 субобъекта будет сопровождаться в Base2 подобъектом, которая будет иметь свои собственные виртуальную таблицы, используя схему, необходимую для Base2, но опять-таки с Base2::b2_fn вместо оригинала.

При отливке Derived указателя на Base2 указатель, компилятор сделает это указует на Base2 подобъект с его виртуальными таблицами, применяя фиксированное смещение определяются во время компиляции.

Кстати, если бы вы сделали понижение, компилятор аналогичным образом использовал бы фиксированное смещение в другом направлении, чтобы найти начало Derived. Все это довольно просто, пока вы не используете виртуальные базы, в которых техника фиксированного смещения больше не работает. Виртуальные базовые указатели должны использоваться, как описано в этом документе. other SO answer

Это Dr.Dobb's article является лучшим объяснением для всех этих макетов с некоторыми хорошими изображениями.

+0

«_Виртуальные базовые указатели должны быть затем использованы» - это только один вариант реализации, а не необходимость – curiousguy

+0

@curiousguy да, конечно! виртуальные базовые указатели и vptr - оба механизма реализации, используемые для реализации стандарта C++, и не относятся к стандарту. Таким образом, можно использовать любую альтернативу для достижения тех же самых эффектов. Ради любопытства, есть ли у вас какой-то конкретный вопрос? – Christophe

+0

Нет никакой жизнеспособной альтернативы vptr, но у виртуальных базовых указателей есть очевидная альтернатива: нет виртуальных указателей.Смещение виртуальной базы находится в таблице vtable. Правила GCC. – curiousguy