2015-06-10 2 views
7

Я читать эту статью «Virtual method table»виртуальная таблица метод множественного наследования

, например, в указанной выше статье:

class B1 { 
public: 
    void f0() {} 
    virtual void f1() {} 
    int int_in_b1; 
}; 

class B2 { 
public: 
    virtual void f2() {} 
    int int_in_b2; 
}; 

class D : public B1, public B2 { 
public: 
    void d() {} 
    void f2() {} // override B2::f2() 
    int int_in_d; 
}; 

B2 *b2 = new B2(); 
D *d = new D(); 

В статье, автор вводит, что макет памяти объекта d заключается в следующем:

  d: 
D* d-->  +0: pointer to virtual method table of D (for B1) 
      +4: value of int_in_b1 
B2* b2--> +8: pointer to virtual method table of D (for B2) 
      +12: value of int_in_b2 
      +16: value of int_in_d 

Total size: 20 Bytes. 

virtual method table of D (for B1): 
    +0: B1::f1() // B1::f1() is not overridden 

virtual method table of D (for B2): 
    +0: D::f2() // B2::f2() is overridden by D::f2() 

Вопрос о d->f2(). Вызов d->f2() проходит B2 указателя как this указателя, поэтому мы должны сделать что-то вроде:

(*(*(d[+8]/*pointer to virtual method table of D (for B2)*/)[0]))(d+8) /* Call d->f2() */ 

Почему мы должны пройти B2 указатель как this указателя не оригинальный D указателя ??? Фактически мы вызываем D :: f2(). Исходя из моего понимания, мы должны передать указатель D как this в функцию D :: f2().

___update____

Если пропускание B2 указателя как this к D :: f2(), что, если мы хотим получить доступ к членам класса B1 в D :: f2() ?? Я считаю, что B2 указатель (это) показано, как это:

  d: 
D* d-->  +0: pointer to virtual method table of D (for B1) 
      +4: value of int_in_b1 
B2* b2--> +8: pointer to virtual method table of D (for B2) 
      +12: value of int_in_b2 
      +16: value of int_in_d 

Он уже имеет определенное смещение адреса начала этого смежного расположения памяти. Например, мы хотим получить доступ к b1 внутри D :: f2(), я предполагаю, что во время выполнения он сделает что-то вроде: *(this+4) (this указывает на тот же адрес, что и b2), который будет указывать b2 в B ????

ответ

4

Мы не можем передать указатель D на виртуальную функцию, переопределяющую B2::f2(), поскольку все переопределения той же виртуальной функции должны принимать идентичный макет памяти.

Поскольку B2::f2() функция ожидает B2 «ы распределения памяти объекта передается к нему в качестве this указателя, т.е.

b2: 
    +0: pointer to virtual method table of B2 
    +4: value of int_in_b2 

функция наиважнейшей D::f2() должна ожидать того же компоновки, как хорошо. В противном случае функции больше не будут взаимозаменяемыми.

Чтобы понять, почему вопросы взаимозаменяемости рассмотрим следующий сценарий:

class B2 { 
public: 
    void test() { f2(); } 
    virtual void f2() {} 
    int int_in_b2; 
}; 
... 
B2 b2; 
b2.test(); // Scenario 1 
D d; 
d.test(); // Scenario 2 

B2::test() потребности, чтобы сделать вызов f2() в обоих сценариях. У него нет дополнительной информации, чтобы сообщить ему, как должен быть отрегулирован указатель this при выполнении этих вызовов *. Вот почему компилятор передает фиксированный указатель, поэтому вызов test()f2 будет работать как с D::f2(), так и с B2::f2().

* Другие реализации могут очень хорошо передать эту информацию; однако многократная реализация наследования, обсуждаемая в статье, не делает этого.

+0

(1) Что вы подразумеваете под «взаимозаменяемым», можете ли вы объяснить немного более подробно? (2) Если передать указатель 'B2' как' this' в D :: f2(), что делать, если мы хотим получить доступ к членам класса B1 в D :: f2()? Для (2), пожалуйста, ознакомьтесь с обновлением вопроса. – Fihop

+0

Большое спасибо! Пожалуйста, подтвердите. 'B2 b2; b2.test() ', это указатель' B2', переданный как 'this' на' B2 :: test() 'определенно, так как' b2' является отдельным объектом. Для 'D d; д.test() ', компилятор передает указатель fixup, который фактически указывает на под-объект' B2' 'D' как' this' to 'test()', поскольку фактически вызывающая функция - 'B2 :: test() '. Если 'this' не указывает на' B2' внутри 'D', это вызовет проблемы при доступе к членам' B2' внутри функции 'B2 :: test()'. Вот почему я думаю, что мы должны передать указатель fixup как 'this'. Этот пример не может объяснить обновление. Спасибо еще – Fihop

+0

Для 'D d; d.test() ', я согласен, мы делаем что-то вроде' d.test (B2 * b2) '(означает, что' this' указывает на под-объект 'B2' of D). Однако внутри 'B2: test',' b2-> f2() 'должно выполняться' D :: f2'. Я прав? Теперь проблема превращается в то, что 'this' передается в' b2-> f2() ' – Fihop

1

Учитывая вашу иерархию классов, объект типа B2 будет иметь следующий объем памяти.

+------------------------+ 
| pointer for B2 vtable | 
+------------------------+ 
| int_in_b2    | 
+------------------------+ 

Объект типа D будет иметь следующий объем памяти.

+------------------------+ 
| pointer for B1 vtable | 
+------------------------+ 
| int_in_b1    | 
+------------------------+ 
| pointer for B2 vtable | 
+------------------------+ 
| int_in_b2    | 
+------------------------+ 
| int_in_d    | 
+------------------------+ 

При использовании:

D* d = new D(); 
d->f2(); 

Этот вызов является такой же, как:

B2* b = new D(); 
b->f2(); 

f2() может быть вызвана с помощью указателя типа B2 или указатель типа D. Учитывая, что среда выполнения должна иметь возможность правильно работать с указателем типа B2, она должна иметь возможность правильно отправить вызов на D::f2(), используя соответствующий указатель функции в таблице v. Однако, когда звонок отправляется на D:f2(), первоначальный указатель типа B2 должен каким-то образом быть смещен должным образом, так что в D::f2(), this указывает на D, а не B2.

Вот ваш пример кода, немного измененный для печати полезных значений указателя и данных члена, чтобы помочь понять изменения значения this в различных функциях.

#include <iostream> 

struct B1 
{ 
    void f0() {} 
    virtual void f1() {} 
    int int_in_b1; 
}; 

struct B2 
{ 
    B2() : int_in_b2(20) {} 
    void test_f2() 
    { 
     std::cout << "In B::test_f2(), B*: " << (void*)this << std::endl; 
     this->f2(); 
    } 

    virtual void f2() 
    { 
     std::cout 
     << "In B::f2(), B*: " << (void*)this 
     << ", int_in_b2: " << int_in_b2 << std::endl; 
    } 

    int int_in_b2; 
}; 

struct D : B1, B2 
{ 
    D() : int_in_d(30) {} 
    void d() {} 
    void f2() 
    { 
     // ====================================================== 
     // If "this" is not adjusted properly to point to the D 
     // object, accessing int_in_d will lead to undefined 
     // behavior. 
     // ====================================================== 

     std::cout 
     << "In D::f2(), D*: " << (void*)this 
     << ", int_in_d: " << int_in_d << std::endl; 
    } 
    int int_in_d; 
}; 

int main() 
{ 
    std::cout << "sizeof(void*) : " << sizeof(void*) << std::endl; 
    std::cout << "sizeof(int) : " << sizeof(int) << std::endl; 
    std::cout << "sizeof(B1) : " << sizeof(B1) << std::endl; 
    std::cout << "sizeof(B2) : " << sizeof(B2) << std::endl; 
    std::cout << "sizeof(D)  : " << sizeof(D) << std::endl << std::endl; 

    B2 *b2 = new B2(); 
    D *d = new D(); 
    b2->test_f2(); 
    d->test_f2(); 
    return 0; 
} 

Выход программы:

sizeof(void*) : 8 
sizeof(int) : 4 
sizeof(B1) : 16 
sizeof(B2) : 16 
sizeof(D)  : 32 

In B::test_f2(), B*: 0x1f50010 
In B::f2(), B*: 0x1f50010, int_in_b2: 20 
In B::test_f2(), B*: 0x1f50040 
In D::f2(), D*: 0x1f50030, int_in_d: 30 

Когда реальный объект используется для вызова test_f2() является D, значение this изменяется от 0x1f50040 в test_f2() до 0x1f50030 в D::f2(). Это соответствует размеру B1, B2 и D. Смещение объекта B2 объекта D - 16 (0x10). Значение в B::test_f2(), a B*, изменено на 0x10 до того, как звонок отправлен в D::f2().

Я собираюсь угадать, что значение смещения от D до B2 хранится в таблице . В противном случае механизм отправки общей функции не может правильно изменить значение this, прежде чем отправлять вызов правой виртуальной функции.