2015-10-08 3 views
2

Простая иерархия виртуальная функция:наследование классов и таблица

class X 
{ 
public: 
    virtual void test(){ printf("x\n");} 
}; 

class Y : public X 
{ 
public: 
    virtual void test() { printf("y\n");} 
}; 

class Z : public Y 
{ 
public: 
    void test() { printf("z\n");} 
}; 

Если я создаю экземпляр г я бы ожидать, что виртуальные таблицы этого экземпляра будет указывать на функцию тестирования Заболоцкого независимо от того, на базе которого я собираюсь лить его в:

Z myZ; 
myZ.test(); 
((Y)myZ).test(); 

Что мне здесь не хватает?

+3

Динамическая отправка происходит только при обращении к объекту с помощью ссылки или указателя. – StoryTeller

ответ

2

Это потому, что вы делаете фактическое преобразование типа myZ. При этом компилятор создаст временный объект (Y)myZ типа Y и будет использовать его для вызова метода test.

Более длинный эквивалент ваш код будет что-то вроде:

Z myZ; 

Y tmp = Y(myZ); 

tmp.test(); 

Вы можете видеть, что это произойдет, если ваш метод test изменяет объект в любом случае. Тогда не должен изменять myZ.

Как указано, динамическая отправка требуется только для указателя или ссылки.

На самом деле вы не можете провести различие между указателем и без указателя, вы могли бы иметь, например, есть метод батут в X что-то вроде этого, и она до сих пор называют Y::test несмотря вы используете указатель:

class X { 
public: 
    void call_test() { 
     this->test(); 
    } 
} 

Тот факт, что динамическая отправка не происходит, можно рассматривать как больше техники оптимизации. Компилятор знает, что ((Y)myZ) будет иметь виртуальную таблицу Y и поэтому может немедленно позвонить Y::test.

2
((Y)myZ) 

с myZ не является указателем нарезки. Таким образом, будет называться Y::test, так как здесь нет информации о Z.

Но вы можете использовать указатели (или ссылки):

Z* z = new Z(); 
    z->test(); 
    Y* y = z; 
    y->test(); 
    delete z; 

z будут напечатаны дважды.

+0

Итак, вы говорите, если мой производный класс не является типом указателя, vtable не рассматривается в приведенном выше случае? Почему C++ обрабатывает типы указателей и не указателей по-разному? И в этом случае нарезка означает просто сокращение всей дополнительной информации, которую класс z добавит к классу y? – binaryguy

+1

@wanderameise Класс не является типом указателя. И C++ обрабатывает указатели и объекты по-разному, потому что именно так был разработан C++. Указатель - это механизм косвенного доступа к объекту. Когда вы вызываете метод непосредственно на объект, вы знаете его точный тип, почему компилятор должен подключаться к коду, чтобы посмотреть в таблице функций метода, а не называть его напрямую? – StoryTeller