Заблокирован
|
||||||
1 | ||||||
Неочевидные грабли полиморфизма с++23.11.2011, 01:05. Показов 13183. Ответов 101
Метки нет (Все метки)
Наткнулся в интернете на любопытный код.
Спешу поделиться с сообществом. Просто, что бы кто если не в курсе - узнал, и не попал на эти грабли:
Но в любом случае, нужно быть предельно осторожным. А по сути, полиморфизм + множественное наследование = мина замедленного действия. Поэтому, при проектировании полиморфных классов, лучше до последнего избегать множественного наследования.
1
|
23.11.2011, 01:05 | |
Ответы с готовыми решениями:
101
Неочевидные особенности выдачи pg таблиц Неочевидные результаты очевидных css-свойств Грабли с кодировкой Xmega грабли |
Заблокирован
|
||||||||||||||||
23.11.2011, 01:42 | 2 | |||||||||||||||
Здесь нет множественного наследования. То есть если рассматривать ваши манипуляции с указателями, то никакого множественного наследования нет.
Дело в том, что указатель типа void * не содержит в себе абсолютно никакой информации об объекте. Именно поэтому он и называется void. Когда вы выполняете первые два предложения из перечисленных трех
Когда же вы тоже самое проделываете для класса C, то компилятор знает, что у этого класса таблица виртуальных функций содержит 3 записи, а потому будет правильно вызывать не только Func3, но и Func2 и Func1. Также очевидно, что dynamic_cast для указателя void * делать бессмысленно, так как тип void * не обладает информацией о классах. Но также бессмыленно делать dynamic_cast и в следующих предложениях
Так что ничего специфического, чтобы можно было говорить о каких-то подводных камнях полиморфизма, в приведенном вами коде нет. Есть лишь безграмотный код. Но любой безграмотный код опасен!
2
|
Заблокирован
|
|||||||||||
23.11.2011, 02:00 [ТС] | 3 | ||||||||||
Однако же, динамическое кастование типов корректно.
Добавлено через 3 минуты
Ни о чем таком не говорит?
2
|
Заблокирован
|
|
23.11.2011, 02:06 | 4 |
dynamic_cast используется для приведения указателя на базовый класс к указателю на производный класс. При противоположном преобразовании можно использовать обычное присваивание, так как происходит неявное преобразование. Компилятор правильно преобразует указатели.
Что касается данного примера, то я не обратил внимание, что это указатель на A, я лишь посмотрел на правую часть выражения, где создавался объект класса C. Мне представляется, что поведение этого кода неопределенное. То есть код некорректный. Другое дело, что в стандарте описано много ситуаций, когда компилятор не обязан выдавать никакого диагностического сообщения о некорректности кода.
0
|
Заблокирован
|
|
23.11.2011, 02:10 [ТС] | 5 |
И потом, класс C здесь - это самый дальний потомок.
Привести тип C к типу A, или B - значит сделать срезку объекта. То есть, если мы сделали приведение объекта C допустим к объекту A, то часть объекта, которая относится к B вылетит в трубу. Что в этот момент произойдёт с таблицей виртуальных функций? На сайте, где я подглядел этот код, была любопытная ссылка: http://insidecpp.ru/test-drive/19/ Добавлено через 2 минуты Я не знаю что такое "указатель на базовый класс". Я знаю что такое "указатель имеющий тип базового класса, который может смотреть куда угодно. Например, на объект имеющий тип любого из потомков типа указателя" И это нужно учитывать, и конкретизировать.
0
|
Заблокирован
|
|
23.11.2011, 02:21 | 6 |
Что такое указатель на объект типа int знаете? Это уже хорошо! Вот тоже самое означает указатель на базовый класс.
Что касается класса C , то не имеет смысла говорить, что он - самый дальний потомок, так как перечисленные вами классы не входят в одну иерархию. Классы A и B никак между собой не связаны. Я могу вам порекомендовать почитать книгу Герба Саттера "Решение сложных задач на С++". Там глава 3 посвещена этим вопросом и так и называется "Разработка классов, наследование и полиморфизм". Он рассматривает похожую ситуацию.
0
|
Заблокирован
|
|
23.11.2011, 02:40 [ТС] | 9 |
Указатель указывает на объект
Причем, далеко не факт, что этот объект будит такого же типа, как и указатель. Он имеет тип, но смотрит всегда на объект Он может иметь тип базового класса, но смотреть на базовый класс не может. Указатель может быть типа int. Указателей на int не бывает
0
|
Заблокирован
|
|
23.11.2011, 02:43 | 10 |
Указатель на int означает, что речь идет о типе указателя. Указатель на объект типа int предполагает, что указатель уже инициализирован адресом этого объекта.
Чтобы вас еще более озадачить, то я вам сообщу, что есть такое выражение, как указатель на член класса, или указатель на функцию.
0
|
Заблокирован
|
|
23.11.2011, 02:48 [ТС] | 11 |
Ну есть разница:
Указатель на int и указатель на объект типа int ? Я могу сказать "указатель на int" - что это значит? Что тип указателя int, или что он смотрит на объект типа int ? Потому что между типом самого указателя, и типом объекта, куда он смотрит жёсткой связи не существует. Он может смотреть на объекты любых типов. Поэтому для указателя очень критично какой тип у самого указателя, и какой на самом деле тип у объекта, на который он указывает. Если это не конкретизировать - можно запросто запутаться. И наступить на грабли.
0
|
Заблокирован
|
|
23.11.2011, 02:50 | 12 |
Bers,
Так как я знаю, что вы упрямый, то, чтобы закрыть вопрос, я вам просто приведу выражение из стандарта С++ the pointer is implicitly converted (4.10) to a pointer to a base class type Но так как я уже предвижу ваши вопросы, то я поясню (что я уже делал ранее), что класс - это тип. Так что в выше указанной фразе слово type можно совершенно спокойно опустить. Я уж не говорю о том, что в стандарте встречается выражение pointer to int ,без добавления слова type.
0
|
Заблокирован
|
|
23.11.2011, 02:55 [ТС] | 13 |
Содержит адрес функции.
По аналогии тоже самое, что сказать "указатель на объект". При этом тип самого указателя здесь вторичен. Указатель на класс - звучит как то не правильно. Указатель на тип... А вот указатель на объект, или функцию, или функцию-член - звучит однозначно. Точно так же, однозначно звучит "указатель типа...", ну например: "указатель типа int" Добавлено через 1 минуту Не по теме: ладно.. буквоедство это все. Я спать пошёл :p
0
|
Заблокирован
|
|
23.11.2011, 03:06 | 14 |
Я вам в своем предыдущем сообщении уже все разъяснил. Просто я его несколько раз корректировал.
Добавлено через 8 минут Bers, Чтобы уж совсем никаких вопросов не было, то я приведу определение из стандарта: A pointer to objects of type T is referred to as a "pointer to T"
0
|
1069 / 848 / 60
Регистрация: 30.04.2011
Сообщений: 1,659
|
|
23.11.2011, 10:30 | 15 |
1. У вас во всех классах - РАЗНЫЕ виртуальные функции.
2. Вы все функции вызываете ЯВНЫМ образом непосредственно по имени. 3. Простое преобразование типа указателя не прокатывает при вызове виртуальных функций. Именно поэтому и был придуман dynamic_cast<>. 4. Вы пытаетесь преобразовать указатель типа A в указатель типа В, который никаким боком с А не связан. Надеюсь, хоть немного прояснилось? Множественное наследование с виртуальными функциями - это действительно ОЧЕНЬ аккуратно надо писать. Добавлено через 4 минуты Добавлю из своей книжки: Отсюда следует, что преобразование одного базового в другой - не работает (указатель A преобразовать в B - невозможно)
1
|
Заблокирован
|
|
23.11.2011, 10:39 [ТС] | 16 |
Вот вы и объясните почему?
Связаны общим потомком. Именно поэтому динамическое кастование корректно Добавлено через 50 секунд Мой тезис только подтверждаеццо: полиморфизм + множественное наследование = мина замедленного действия.
0
|
1069 / 848 / 60
Регистрация: 30.04.2011
Сообщений: 1,659
|
|
23.11.2011, 10:42 | 17 |
Bers,
Нет тут перекрестного преобразования от базового к базовому.
0
|
Заблокирован
|
|
23.11.2011, 10:46 [ТС] | 18 |
Меня не интересует, есть там перекрестки, или нет. Меня интересует, почему динамик_каст корректно приводит, а приведение в стиле си - не корректно.
И что происходит с объектом, в момент приведения его типа к одному из базовых (срезка). По русски, с толком, с расстановкой. Не? Я вам намекну, динамик_каст - дорогое приведение, потому что юзает технологию определения типа объекта в рантайме.
0
|
Заблокирован
|
|
23.11.2011, 10:47 [ТС] | 20 |
не хотите терять производительность - не юзайте динамик_каст. А значит, не юзайте множественное наследование с полиморфизмом. Ваш К. О.
0
|
23.11.2011, 10:47 | |
23.11.2011, 10:47 | |
Помогаю со студенческими работами здесь
20
Грабли с USART_FLAG_RXNE Наступлю на те же грабли? Грабли с WM_DEVICECHANGE Грабли malloc/free Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |