2015-07-01 2 views
2

В книге «Inside the C++ Object Model» указано, что производный класс имеет n - 1 дополнительных виртуальных таблиц, что n - это число базовых классов. Я провел несколько экспериментов с компилятором g ++, а мой компьютер - 64 бит. Ниже приведен мой код.Как компилятор g ++ реализует множественное наследование с виртуальными функциями?

class A{ 
    private: 
     int a; 
    public: 
     virtual void print() const{ 
      cout << a << endl; 
     } 

}; 

class B : A{ 
    private: 
     int b; 
    public: 
     virtual void set(int num){ 
      b = num; 
     } 
}; 


class C : public A, public B{ 
    private: 
     int c; 
    public: 
     void print() const{ 
      cout << c << endl; 
     } 
     void set(int num){ 
      c = num; 
     } 
}; 

И проверить расположение в памяти каждого класса в БГД:

(gdb) p a 
$1 = (A) { 
    _vptr.A = 0x400bb0 <vtable for A+16>, 
    a = 0 
} 
(gdb) p b 
$2 = (B) { 
    _vptr.B = 0x400b90 <vtable for B+16>, 
    b = 4196384 
} 
(gdb) p c 
$3 = (C) { 
    <A> = { 
    _vptr.A = 0x400b50 <vtable for C+16>, 
    a = 4197101 
    }, 
    <B> = { 
    _vptr.B = 0x400b70 <vtable for C+48>, 
    b = 0 
    }, 
    members of C: 
    c = 0 
} 

Это показывает, что есть два vptrs в классе С. Тогда я компилирую его получить с -fdump-class-hierarchy, и я получаю некоторую информацию ,

Vtable for A 
    A::_ZTV1A: 3u entries 
    0  (int (*)(...))0 
    8  (int (*)(...))(& _ZTI1A) 
    16 (int (*)(...))A::print 

    Class A 
     size=16 align=8 
     base size=12 base align=8 
    A (0x0x7fe4654956c0) 0 
     vptr=((& A::_ZTV1A) + 16u) 

    Vtable for B 
    B::_ZTV1B: 3u entries 
    0  (int (*)(...))0 
    8  (int (*)(...))(& _ZTI1B) 
    16 (int (*)(...))B::set 

    Class B 
     size=16 align=8 
     base size=12 base align=8 
    B (0x0x7fe465495720) 0 
     vptr=((& B::_ZTV1B) + 16u) 

    Vtable for C 
    C::_ZTV1C: 7u entries 
    0  (int (*)(...))0 
    8  (int (*)(...))(& _ZTI1C) 
    16 (int (*)(...))C::print 
    24 (int (*)(...))C::set 
    32 (int (*)(...))-16 
    40 (int (*)(...))(& _ZTI1C) 
    48 (int (*)(...))C::_ZThn16_N1C3setEi 

    Class C 
     size=32 align=8 
     base size=32 base align=8 
    C (0x0x7fe46516e690) 0 
     vptr=((& C::_ZTV1C) + 16u) 
     A (0x0x7fe465495780) 0 
      primary-for C (0x0x7fe46516e690) 
     B (0x0x7fe4654957e0) 16 
      vptr=((& C::_ZTV1C) + 48u) 

Тогда я смущен следующими вопросами.

  1. Class A size=16 align=8 base size=12 base align=8 Я не понимаю, почему размер класса А 16 и что базовый размер и выровняйте в виду? Я думал, что размер класса A должен быть 12 байтов в случае 8 байтов для vptr и 4 байта для int a. То же самое для класса B.
  2. Я думал, что есть класс 2 vtables класса C. Но результат показал только один из 7 записей. какова связь между этим vtable и двумя vptr класса C?
  3. Последняя строка показала, что vptr=((& C::_ZTV1C) + 48u), что означает 48? и последней записи vtable класса C показали, что 48 (int (*)(...))C::_ZThn16_N1C3setEi что это значит? это не имя виртуальных функций, а смещение сверху. почему второй vptr натолкнулся на эту запись.
+0

относительно вопроса 1, вам нужно взглянуть на прокладку структуры. Ваша арка ПК выравнивается по 16 байт. int занимает 4 байта, а указатель функции на печать занимает 8 байтов, поэтому 12 байт для базового размера, а последние 4 байта предназначены для выравнивания данных. (Https://en.wikipedia.org/wiki/Data_structure_alignment) – lucasg

ответ

0

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

Приведение указателя объекта к некоторым из его родительских классов может потребовать добавления некоторого смещения (чтобы получить правильные функции vtable &).

Смежные вопросы