Каким образом выбирается версия функции, принимающей указатель на полиморфный класс?

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Открой для себя Visitor Design Pattern. Он позволяет добавлять свои обработчики извне.
2B OR NOT(2B) = FF
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Я уже написал два раза. Напишу третий раз. Компилятор определяет то, какую функцию нужно вызвать исключительно по типу передаваемого выражения.

В той версии кода, где объекты напрямую передаются в функцию, будут вызываться разные функции, так как каждый объект имеет свой тип.

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

И да, паттерн Visitor тебя действительно выручит в этом случае.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Я уже написал два раза. Напишу третий раз. Компилятор определяет то, какую функцию нужно вызвать исключительно по типу передаваемого выражения.
По Страустурупу функция, принимающая указатель на экземпляр полиморфного класса с хотябы одной виртуальной функцией выбирается не по формальному типу указателя, а принимающая указатель на экземпляр фактического типа самого объекта, если же такой версии нет, то выбирается функция, принимающая указатель на экземпляр ближайшего предка фактического типа самого объекта. Это единственное исключение из правила. Но фактический тип объекта, адресуемого указателем, определяется в рантайме. Каким образом? И как в этом случае реализуется выбор функции? Если формальный класс объекта не имеет виртуальных функций, тогда функция выбирается по формальному типу указателя.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Сионист писал(а):По Страустурупу функция, принимающая указатель на экземпляр полиморфного класса с хотябы одной виртуальной функцией выбирается не по формальному типу указателя, а принимающая указатель на экземпляр фактического типа самого объекта
Прошу ссылку на страницу из книги Страуструпа. Никогда такого не было и быть не может. Хотя бы потому, что нет гарантированного способа определить, что именно лежит по указателю на этапе компиляции. Именно на этапе компиляции, и никак иначе.

А про рантайм ты вообще зря завёл разговор, так как позднее связывание возможно только для виртуальных методов, и для этого используется специальный механизм, обременяющий нас memory overhead'ом (каждый объект содержит в первых байтах указатель на таблицу виртуальных функций, с помощью которой и происходит определение нужного метода). Думаю, именно с этим механизмом ты и путаешь вызов обычных функций.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Но фактический тип объекта, адресуемого указателем, определяется в рантайме. Каким образом? И как в этом случае реализуется выбор функции? Если формальный класс объекта не имеет виртуальных функций, тогда функция выбирается по формальному типу указателя.
В класс добавляется неявный указатель на массив указателей на функции. При этом для каждой виртуальной функции во время компиляции генерируется уникальный индекс в этом массиве. Обычно у виртуального деструктора он 0 или даже -1 если указатель указывает не на первый, а на второй или третий элемент. Например, RTTI может быть реализовано через виртуальную функцию с индексом -2, виртуальный деструктор имеет индекс -1, а первый виртуальный метод может иметь индекс 0. В рантайме по этому индексу выфетчивается указатель на функцию, и полученная функция вызывается с нужными параметрами которые известны во время компиляции. У каждого полиморфного типа этот массив указателей свой собственный. То что я сейчас сказал Стандартом не оговаривается, но другого способа все это реализовать по видимому нет. Если массив поменять на какой-нибудь хештейбл, то это будет соответствовать Стандарту, но скорость упадет очень заметно.
2B OR NOT(2B) = FF
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Absurd, всё что ты написал, справедливо для виртуальных методов. Я тоже об этом написал выше.

Сионист же уверен, что полиморфизм работает и для обычных функций, принимающих указатель на базу. Мои аргументы о том, что это попросту невозможно, не помогают.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

И с какого оно не возможно то? Вот использовать это для сторонних функций походу нельзя: если функции и конструкторы находятся в разных проектах, то конструкторы не имеют информации об адресах функций и не могут внести их ни в какую таблицу, где бы она ни валялась.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

В момент компиляции никаких адресов нет. Есть символы. Часть символов приобретает адреса в момент линковки, часть - во время загрузки DLL.
2B OR NOT(2B) = FF
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Сионист писал(а):И с какого оно не возможно то? Вот использовать это для сторонних функций походу нельзя: если функции и конструкторы находятся в разных проектах, то конструкторы не имеют информации об адресах функций и не могут внести их ни в какую таблицу, где бы она ни валялась.
Ты будешь продолжать голословно утверждать невозможное, или всё-таки предоставишь пруфлинк? Поверь мне, ты просто не найдёшь такого в стандарте.

Хорошо, давай объясню, почему невозможно. Для того, чтобы это работало по подобию виртуальных методов, мы должны для всех внешних функций создать таблицу и разместить её указатель в объект. Какие тут возникают проблемы?

Первая проблема в том, что мы не знаем нужна ли нам поддержка полиморфного поведения для функций или нет. Для методов есть ключевое слово virtual, по которому компилятор определяет нужно ли добавлять метод в таблицу и нужно ли добавлять указатель на таблицу в объект. Для внешних функций такого ключевого слова не предусмотрено.

Хорошо, предполагаем, что мы будем создавать такую таблицу ВСЕГДА, помещать в неё ВСЕ внешние функции, хоть это и будет оверхедом по памяти. Кстати говоря, это бы обозначало, что в КАЖДОМ объекте должен быть ВСЕГДА указатель на таблицу внешних функций. Ты о таком слышал? Я - нет. Ладно, чёрт с ним, идём дальше.

Как известно, объявление класса МОНОЛИТНО. То есть если мы объявляем класс, то мы тут же обязаны описать ВСЕ его методы и поля. Доопределить класс в другой единице трансляции или в другом модуле (например добавив в него ещё один метод) нельзя. Благодаря этой особенности компилятор может построить таблицу виртуальных методов и задать каждому методу свой индекс (ведь таблица устанавливает соответствие между порядковым индексом метода и его адресом в сегменте кода). Что у нас будет в случае внешних функций? Как нам определить, какая функция будет в первой ячейке, какая во второй? Если внутри отдельного модуля ещё как-то можно решить эту проблему, создав огромную таблицу для всех функций, раздав им индексы в этой таблице в произвольном (точнее лишь одному компилятору ведомом) порядке, то при появлении новой перегруженной функции, расположенной в другом модуле, всё рушится, как карточный домик. Ведь основной модуль уже скомпилирован, таблицу уже нельзя перестроить. Напомню, что у классов объявление монолитно, потому там подобной проблемы нет.

Ещё раз говорю, что ты пытаешь притянуть за уши невозможное, да ещё и заявляешь, что об этом написано у Страуструпа. Не стыдно?
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Duncon
Сообщения: 2085
Зарегистрирован: 10 окт 2004, 14:11
Откуда: Питер
Контактная информация:

Может сионист просто троллит?
[syntax=Delphi] [/syntax]
Ответить