| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > C/C++: Общие вопросы > RTTI |
| Автор: Graf_h 24.9.2009, 22:38 |
| Привет. Интересует вопрос, как компилятор понимает тип обьекта (по каким именно критериям)? Как происходит приведения типов. Почему любой указатель можно привести к *void. Как в иерархии наследования компилятор знает родителей и т.д. Заранее спасибо |
| Автор: jonie 24.9.2009, 22:45 | ||
на многие вопросы можно найти ответы тут: http://www.rsdn.ru/article/cpp/fastdelegate.xml |
| Автор: mes 25.9.2009, 11:42 |
Программист определяет тип объекту, а компилятор его ассоциацивно запоминает или выводит из имеющихся данных. Так договорились, для удобства так же как и в первом случае, потому что программист указал это : class Derived: public Base {}; Посмотрел на название темы RTTI, значит интересует dynamic_cast ? один из вариантов реализации, для сравнения типов объектов, сравнивать указатель на виртуальную таблицу. |
| Автор: Graf_h 25.9.2009, 14:06 | ||
| В принципе вообще можно обойтись только функцией memcpy, ведь в исполняемом файле нету такого понятия, там просто набор байтов. Вот провел небольшой тест:
результаты: 1 2 11 21 1 2 Зачем нам тогда static_cast dynamic_cast и т.д. |
| Автор: bsa 25.9.2009, 14:38 |
| Graf_h, копирование с помощью memcpy допустимо только для POD объектов. Сложные объекты (например, конструкторы которых выделяют память, а деструкторы ее освобождают) копировать так НЕЛЬЗЯ! |
| Автор: Static 25.9.2009, 16:30 | ||
| 2bsa Я правильно понимаю сказанное? Например:
|
| Автор: Graf_h 25.9.2009, 16:58 |
| Static, bsa правду говорит. Скопировать то можно, но тогда неизбежны утечки памяти. Хотелось бы все же узнать как компилятор знает тип обьекта(имеется ввиду dynamic_cast)?? |
| Автор: mes 25.9.2009, 22:07 | ||||||
да, по этой самой причине нельзя примянять к объектам таких классов динамик_каст.
Затем чтоб избавить программиста от ручных преобразований и дать ему по рукам если компилятор в силах определить ошибочность.
компилятору не нужно знать реальный тип объекта в реале, главное может ли произойти нужное преобразование, т.е. (для динамик_каста) можно ли привести имеющийся указатель к тому который запросил программист. В виртуальной таблице (ВMТ) наследника, хранится указатель на ВMТ предка, и этим образуют "список наследования" осталось только проверить находится ли указатель на ВMТ в нужном списке наследования и если нет, то вернуть ноль, это и делает dynamic_cast. |
| Автор: Graf_h 25.9.2009, 22:50 | ||||
mes большое спасибо +1. Еще хотелось бы уточнить насчет в-табл.
Это по сути 2 разные функции, 2 разных адресса. Как компилятор знает что это переопределенная функция??
каким образом здесь вызывактся нужная функция(класса В). Ведь по сути класс А совсем ничего не знает о своем потомке. Хотя, с другой стороны, это адресс обьекта B. Почему тогда я не могу вызвать функции которые не описаны в родителе? |
| Автор: mes 25.9.2009, 23:34 | ||
вызов виртуальной функции pa->f() грубо говоря представлен как (pa-> pa->vmt-> f)(); где pVMT указатель на виртуальную таблицу (устанавливается в конструкторе) который для каждого класса другой. |
| Автор: Graf_h 25.9.2009, 23:47 |
| mes, я видимо что то не доганяю. Если у нас есть виртуальная функция func() в родителе, то в потомке такая же функция будет совсем по другому адресу(другая функция с тем же именем). Вопрос собсно в том как компилятор знает что это именно та функция которую нужно переопределить? |
| Автор: mes 26.9.2009, 00:05 | ||
можете, надо явно указать тогда чей метод хотите вызвать. Судя по Вашим постам, основное заблуждение в том, что Выт думаете что компилятор при разборе видит только ту строчку которую он разбирает, и что вся нужная информация содержится в ней. На самом деле очень много "грязной" информации программист не видит, и это упрощает написание кода, однако затрудняет понимание самого процесса происходящего, в случае если программист не знает как все это на самом деле работает. /* В частности поэтому многие недолюбливают Си++: одни из них (обычно сишники с опытом) прекрастно представляют как это работает и могут легко получать это обычными си-средствами, а так как привыкли все и вся контролировать сами, помощь компилятора им просто излишна. другие не понимают что творится, но видят что на других языках, в том числе и на Си таких замудреностей нет, поэтому боятся Си++ и избегают его. */ Так вот когда метод указан как виртуальный, вместо вызова встраивается дополнительный код, который вызывает нужную функцию (опираясь на VMT) Добавлено @ 00:13 Да но эта функция при виртуальном вызове не будет вызвана напрямую. Адрес каждой из них хранится в своей VMT, а объект при конструировании всегда получает указатель на "свою" VMT, относительно которой и происходит вызов. Добавлено @ 00:17 Вот здесь был приведен пример по иллюстрации работы виртуального механизма : http://forum.vingrad.ru/index.php?showtopic=212803&view=findpost&p=1524278 |
| Автор: Graf_h 26.9.2009, 09:15 | ||
Большое спасибо за ссылку, и за то, что вы есть на этом форуме |
| Автор: mes 28.9.2009, 20:14 | ||
Ну хотя бы вкратце показали бы где |
| Автор: mes 31.10.2009, 21:15 | ||||||||
хотелось бы уточнить что не верно .. под фразой "нельзя применять" подразумевалось, что нельзя получить ожидаемый (т.е такой какой был бы в случае обладания классом виртуальной функции) результат, а не то что применение каста вызовет ошибку компиляции либо рантайма.. Т.е. "нельзя" в роли "бесполезно", a не "невозможно" или "строжайше запрещено" все еще неверно ? или речь про это ?
т.е. то что отсутствие именно виртуальной таблицы упоминается как причина ? P.S.по-мойму форум глючит .. старые темы с новыми датами всплыли.. и комменты некоторые почему то изменились.. |
| Автор: UnrealMan 1.11.2009, 01:16 |
Чтобы говорить о требованиях к операнду dynamic_cast и внутренней реализации преобразования, надо бы уточнить, какое именно преобразование рассматривается (а в случае с реализаций ещё и компиляторы). dynamic_cast не всегда использует RTTI. |
| Автор: mes 1.11.2009, 14:16 | ||||||||
A..ну теперь вроде понятно что куда Вы клоните..Перечитал тему еще раз - действительно выглядит не связано.. Попробую расставить на свои места : Вначале я думал что топикстартера интересует : ответом было :
после, заметив слово "RTTI" в названии, я предположил что интересует обратное преобразование (т.е. downcast), потому как для приведенного в примере информации достаточно и на этапе компиляции.. Но явно я забыл это указать, (если быть точнее я не хотел запутывать тс еще больше), сократив мысль до такой : и в дальнейшем dynamic_cast подразумевался именно как инструмент downcasta. Теперь попытаюсь исправить те два вранья :
следует читать : нельзя произвести downcast с проверкой рантайма к объектам таких классов.. сам же dynamic_cast однако можно использовать для их (объектов) upcasta и crosscasta.
Естественно само описание с участием ВМТ используются условно как частный случай реализации. UnrealMan, я правильно понял, что Вы имели ввиду ? |
| Автор: UnrealMan 1.11.2009, 16:19 | ||||
RTTI может ещё использоваться для crosscast'а и приведения к void*. Для upcast'а RTTI не нужна.
А если мы к ссылке приводим, то в этом случае тоже нулевой указатель может возвращаться? P.S. А-а-а, мне запретили рейтинговать |
| Автор: mes 1.11.2009, 18:16 | ||||
спасибо, этого не знал и даже не подозревал.. Надо будет покапать в этом направлении..
В предыдущем посте я пытался донести, что обсуждении с тс было посвящено не возможностям dynamic_cast и требований к этому оператору, более того сам dynamic_cast имел отношение к разговору только лишь потому, что является инструментом для некоторых преобразований.. А обсуждалось каким образом компилятор может узнать о возможности правильного преобразования одного типа к другому (в том числе и в рантайме). А так я в курсе, что неудачный dynamic_cast для ссылки возбуждает исключение. |
| Автор: DrHex 2.11.2009, 00:23 | ||||||||||||||||||||||||||||||
| Книжная теория это всего лишь слова. Пример vs2008
На выходе в релизной версии имеем
Для тех кто не понимает
Происходит заряд виртуальной таблицы....
Пырвый вызов функции (a->func1();) Так как указатель на функцию идет первым(dword ptr [eax],offset C::`vftable' (402138h) ) соответственно вызов происходит по указателю(в нашем без смещений от объекта, то и есть кастинг бых выполнен в стиле "С")
esi+8 смещение на вось байт. Почему на восемь??? указатель на функцию(4 байта) + поле (4 байта) и того восемь. То и есть приведение было в стиле "С" и смещение на восемь байт. В дебажном варианте тоже не всегда можно удидеть RTTI
Ключевой момент(Смещение указателя)
Вот другое дело что взять да и убрать public наследование то и есть меняем
на
И видем картину A* a = dynamic_cast<A*>©;
___RTDynamicCast
Стандартный для студии пролог....
Установка SEH(catch)
Есче(еще для умных) SEH. Опистим детали(это же не учебник) Один из ключевых моментов FindMITargetTypeInstance должен вернуть указатель на объект
jmp __RTDynamicCast+0F1h делает переход на
Как видим функция должна вернуть не ноль что бы условного переход не было, если условный переход(je __RTDynamicCast+112h (102B33B2h) ) произойдет, то будет вызвана ошибка(генерация исключения на сколько помню...) |
| Автор: Graf_h 15.4.2010, 10:33 | ||
mes скажите пожалуйста, как можно получиь таблицу парента, по текущей вирт таблице. Я как не пыался, не получилось ( |
| Автор: mes 15.4.2010, 11:26 |
даже наличие таблицы, не говоря о том как она устроена - implementation defined, а значит вопрос должен как минимум упоминать конкретный компилятор. ну а если в рамках пользователя С++, то такой вопрос не должен интересовать. для этого нужно знать структуру VMT, ну и по какому смещению она расположена (для нахождения текущей) |
| Автор: Graf_h 15.4.2010, 15:08 | ||
Почему? Если, к примеру, мне нужен свой dynamic_cast? |
| Автор: mes 15.4.2010, 15:38 |
потому что такое возможно либо узкоспециализированное - под каждый компилятор свое, либо составлять свою таблицу - такой путь предпочтительней.. |
| Автор: Graf_h 15.4.2010, 18:56 |
| mes, спасибо |