Здравствуйте, erithion aka tyomik, Вы писали:
EAT>Забыл упомянуть, что вызов невиртуальных методов всегда происходит непосредственно, по адресу, за исключением сложных случаев наследования, когда невиртуальный метод таки попадает в виртуальную таблицу и вызывается косвенно. Но такие случаи не часты.
А можно пример, когда такое возможно?
Chez, ICQ# 161095094
Re[4]: Вызов виртуального метода для класса
От:
Аноним
Дата:
20.10.04 07:53
Оценка:
Здравствуйте, jazzer, Вы писали:
J>Потому что Стандарт С++ не оговаривает машинный код методов, более того, в Стандарте нет понятия виртуальной таблицы. (попробуй поискать в тексте Стандарта vtbl или vmt на досуге) J>Ты оперируешь терминами конкретной реализации, выходящими за пределы Стандарта, а задаешь вопрос о решении в рамках Стандарта. J>Решения в рамках Стандарта не существует. J>Существуют платформенно-зависимые решения, начиная с "определенного" неопределенного поведения и кончая asm.
Здравствуйте, Chez, Вы писали:
C>Здравствуйте, erithion aka tyomik, Вы писали:
EAT>>Забыл упомянуть, что вызов невиртуальных методов всегда происходит непосредственно, по адресу, за исключением сложных случаев наследования, когда невиртуальный метод таки попадает в виртуальную таблицу и вызывается косвенно. Но такие случаи не часты. C>А можно пример, когда такое возможно?
Пожалуйста:
class a
{
public:
a()
{
printf("a::constructor");
}
virtual void fn()
{
printf("a::fn");
}
};
class c: public virtual a
{
public:
c()
{
printf("c::constructor");
}
void fn()
{
printf("c::fn");
}
};
int _tmain(int argc, _TCHAR* argv[])
{
c* i = new c();
i->fn();
delete i;
return 0;
}
Вот более или менее понятный разбор того, что происходит в бин. коде:
.rdata:0040811C c_vtbl dd offset c_fn ; DATA XREF: c_constructor+2Co
.rdata:00408120 init dd 0 ; DATA XREF: c_constructor+10o
.rdata:00408124 dd 8
.rdata:00408138 a_vtbl dd offset a_fn ; DATA XREF: a_constructor+Ao
Вкратце, происходит следующее:
Внутри конструктора класса с видим, что он в самое начало вирт. таблицы записывает некую таблицу индексов, которой потом пользуется для инициализации и позднее поиска виртуальных таблиц и ф-й в них. Мои комментарии справа подразумевают, что в таблице смещения не в байтах, а в DWORD'ах, т.е. индексы.
Далее происходит вызов конструктора класса а, который записывает во 2-й элемент таблицы адрес вирт. таблицы класса а. После этого происходит замещение этого адреса адресом вирт. таблицы класса с, которая вмещает в себя невиртуальную ф-ю в классе с. После выполняется все остальное тело конструктора.
В main можно увидеть, как вызов i->fn(); происходит косвенно через регистр еах.
P.S.: Спасибо за вопрос. Это заинтересовало меня, поскольку в VS 2003 .NET сишный компилер генерит несколько иной код. В частности меня заинтересовал единственный параметр в конструкторе класса с равный 1. В конструкторе же можно видеть, что ежели этот параметр равен нулю, то вызова конструктора базового класса не произойдет. Пока я не знаю к какой опере это относится и как можно добится того, чтоб конструктор предка не вызывался вовсе. Кроме того, при обычном наследовании(невиртуальном) у конструктора отсутствует этот параметр вообще. Так что это повод провести более полный анализ.
Thx!!!
Здравствуйте, erithion aka tyomik, Вы писали:
EAT>>>Забыл упомянуть, что вызов невиртуальных методов всегда происходит непосредственно, по адресу, за исключением сложных случаев наследования, когда невиртуальный метод таки попадает в виртуальную таблицу и вызывается косвенно.
c::fn — виртуальная функция.
EAT>P.S.: Спасибо за вопрос. Это заинтересовало меня, поскольку в VS 2003 .NET сишный компилер генерит несколько иной код. В частности меня заинтересовал единственный параметр в конструкторе класса с равный 1. В конструкторе же можно видеть, что ежели этот параметр равен нулю, то вызова конструктора базового класса не произойдет. Пока я не знаю к какой опере это относится и как можно добится того, чтоб конструктор предка не вызывался вовсе. Кроме того, при обычном наследовании(невиртуальном) у конструктора отсутствует этот параметр вообще. Так что это повод провести более полный анализ.
Виртуальные базовые классы инициализируются только из конструктора most derived class.
Здравствуйте, elcste, Вы писали:
E>Здравствуйте, erithion aka tyomik, Вы писали: E>c::fn — виртуальная функция.
Еще раз повторюсь, меня не интересуют стандарты, несмотря на то, что я тоже читал Страуструпа и другую не менее интересную литературу и тоже прекрасно помню, что всеми ими такой метод класса обзывается виртуальным.
С точки же зрения исследователя, я знаю другое, а именно, что это чистый метод класса в реализации МС С++ компилятора. Его виртуальность возникает вследствие перекрытия имен, которая разрешается в пользу виртуальности метода наследника. И виртуальность его косвенная, поскольку реализуется через переходник. На что собсно код и указывает. В виртуальной таблице класса с не адрес самой процедуры, а адрес так называемого переходника на процедуру(метод класса c::fn) с коррекцией указателя перед переходом. Обрати внимание еще раз, мож теперь понятнее буит:
Отсюда видно, что это есть механизм, с помощью которого возможна реализация положений стандарта и возникает он только при виртуальном наследовании. На самом же деле это есть обычный метод класса.
E>Виртуальные базовые классы инициализируются только из конструктора most derived class.
Я сказал про параметр, вследствие равенства нулю которого вызова конструктора базового класса вообще не происходит. Как бы я не наследовался, вызов все равно будет, т.е. если присутствует один параметр в конструктор, то пока что у меня он всегда был равен 1. Равенства же его 0 я пока не смог добиться. И пока не упомню ситуаций, когда такое возможно. Разве что для нужд компилятора в специфических ситуациях, когда тока так можно выйти из положения.
E>P.S. Учите язык.
Спасибо! Торжественно обещаю
А какой? Алгол?
Я слышал множество пунктов стандарта и предложений по реализации взято именно оттуда.
Здравствуйте, erithion aka tyomik, Вы писали:
E>>c::fn — виртуальная функция.
EAT>Еще раз повторюсь, меня не интересуют стандарты, несмотря на то, что я тоже читал Страуструпа и другую не менее интересную литературу и тоже прекрасно помню, что всеми ими такой метод класса обзывается виртуальным.
То есть эту функцию невиртуальной называете только Вы. Или еще кто-то?
EAT>С точки же зрения исследователя, я знаю другое, а именно, что это чистый метод класса в реализации МС С++ компилятора.
Вот только меня, в свою очередь, совершенно не интересует "реализация МС С++ компилятора". В данный момент я работаю с процессором Blackfin (ADSP-BF561), и мне интересен код, который генерирует VisualDSP++ от Analog Devices. Не хотите исследовать его для разнообразия?
Обратите внимание на то, что вызов виртуальной функции c::fn происходит косвенно, через таблицу виртуальных функций, несмотря на совпадение в данном случае статического и динамического типов указуемого i объекта.
EAT>Его виртуальность возникает вследствие перекрытия имен, которая разрешается в пользу виртуальности метода наследника.
Bravo! You made my day!
E>>Виртуальные базовые классы инициализируются только из конструктора most derived class. EAT>Я сказал про параметр, вследствие равенства нулю которого вызова конструктора базового класса вообще не происходит.
Прочитав учебник, Вы осознаете смысл сказанного выше. Если же Вы принципиально не читаете ничего, кроме ассемблерных листингов, помедитируйте над результатом трансляции следующего примера.
Здравствуйте, elcste, Вы писали:
E>То есть эту функцию невиртуальной называете только Вы. Или еще кто-то?
Я б сказал, что для меня(и не только) она таковой не является именно потому, что стандартный подход реверсирования в этом случае несколько изменяется. Именно изза наличия переходника.
E>Вот только меня, в свою очередь, совершенно не интересует "реализация МС С++ компилятора". В данный момент я работаю с процессором Blackfin (ADSP-BF561), и мне интересен код, который генерирует VisualDSP++ от Analog Devices. Не хотите исследовать его для разнообразия?
Нет, спасибо, пока не хочу.Поскольку меня ждет не менее интересная работа с кодом ARM-процессора и тонкостями его построения компилятором.
Но раз уж речь зашла об этом, то компилер С++ для ARM'а вообще не признает виртуальное наследование. А вызов виртуальных методов производит непосредственно или же с jump'ом на код коррекции адреса перехода.
Так что не думаю что стоило здесь приводить подобные листинги, поскольку разговор шел о МС С++ в частности, и в целом для х86, если не ошибаюсь.
EAT>>Его виртуальность возникает вследствие перекрытия имен, которая разрешается в пользу виртуальности метода наследника.
E>Bravo! You made my day!
Я рад что тебе понравилось. Зови в следующий раз, посмешу
E>>>Виртуальные базовые классы инициализируются только из конструктора most derived class. EAT>>Я сказал про параметр, вследствие равенства нулю которого вызова конструктора базового класса вообще не происходит.
E>Прочитав учебник, Вы осознаете смысл сказанного выше. Если же Вы принципиально не читаете ничего, кроме ассемблерных листингов, помедитируйте над результатом трансляции следующего примера.
За это спасибо. Это как раз то, что мне было нужно. Ответ прозвучал и раньше, но боюсь я его не сразу понял.
Так что иду к стандартам по виртуальному наследованию и не только