Kartonagnick
>Вот с этого момента поподробнее, пожалуйста.
Т.е. вред от такого подхода не пугает.
хорошо.
1. Из-за того что c++ работает исключительно с объявленными типами, делаем прототип функции
void *Test();
после
Base* ptr = new Derived;
затем Test=ptr+testshift;// где testshift смещение функции test от начала ptr.
// а как его высчитать - изучаем документацию на конкреный компилятор.
затем просто вызываешь *Test() и вызовется (ptr->clone())->Test(). Типа Derived.
Подробнее нужно читать об организации памяти для указатели, указатели на функции, указатели на классы, организацию памяти для объекта. И у каждого компилятора свои нюансы,
!НО. Так делать крайне не рекомендуется.
1. Это уход от приципов C++
2. Вероятность выпустить бажный код - 99.999%.
Данную херню можно попробовать только для самообучения и не более.
Сам компилятор создали чтобы все эти смещения высчитывались автоматом, чтобы в итоговой программе было минимум ошибок. А чтобы лазить из базового класса к потомкам придумали виртуальные функции и dinamic_cast.
KKH
> затем Test=ptr+testshift;// где testshift смещение функции test от начала ptr.
Нее, это фигня какая-то. Адрес метода же не в самом объекте хранится.
[A][R][T]
Не буду спорить, т.к. достоверно не помню. Но если функции (методы) и переменные статические (хотя какая разница, на динамические указатели тоже внутри объекта), разве он их не подряд в одну память кладёт при создании очередного объекта ? Возможно я отстал от жизни ... Смысл хапать память кусками из разных областей ... её даже кешировать не удобно тогда.
Я баловался раньще, возможно мне везло. К ptr прибавлял по 4 и получались все указатели начиная с первой функции и заканчивая последней переменной. это был MSVC.
>Адрес метода же не в самом объекте хранится.
Что-то [A][R][T] я в этом сильно сомневаюсь. Лень на это тратить суботу, чтобы вспомнить механизм.
Помню одному человеку очень дого доказывал что в dll можно выделить память а в exe освободить. Кончилось тем что он не верил в рабоу программы. И очень дого цепялся к тому что я пользовался не new и delete. И в доказательство своей правоты, просил это сделать new и delete. Так что с тех пор тратить время на споры - не хочу.
Адрес виртуального метода, как известно, в вптр объекта хранится, а для остального можно сразу адреса подставлять, что компиляторы и делают. Но ЕМНИП в стандарте это не обговорено.
Резюмируя: через указатель на базовый класс, невозможно толкнуть не_виртуальный метод потомка, через "смещение".
Просто потому, что объект-потомок не хранит внутри себя информацию о своих не_виртуальных методах.
Kartonagnick
> Резюмируя: через указатель на базовый класс, невозможно толкнуть не_виртуальный
> метод потомка, через "смещение".
Зачем смещение?
http://ideone.com/5kaBx - вызвали функцию одного класса, используя объект другого класса.
P.S. Возможно, не самый удачный пример, так как в качестве указателя на объект в данном случае можно использовать хоть NULL. Но так как идея всё равно ничего общего с практикой не имеет, зато представление даёт, то исправлять не буду.
Zefick
> Возможно, не самый удачный пример
Это не самый удачный пример, потому что там используется явное приведение типов.
То бишь, программист сам ручками указывает метод какого класса он толкает.
А нужно толкнуть не_виртуальный метод объекта, ничего не зная о его реальном типе.
--------------
Проблема на самом деле глубже, чем кажется. Проиллюстрирую на примере:
Что бы вытащить на свет божий "реальный тип" объекта в рантайме, нужно как минимум запустить нечто, имеющее вид:
template<class type> type& GetFact(void* pTarget) { return *( ( type*) pTarget); }
--------------
Проблема не в том, как идентифицировать сам тип объекта. А в том, как этот самый тип вытащить наружу?
Допустим, есть такая лажа:
switch (ID) { case( TYPE_Derrived): { /*и чего? И как? Ну узнали вы, что тип - derived, что дальше? Как этот тип выкинуть наружу?*/ } }
Допустим, мы можем запомнить адрес функции GetFact<TYPE_Derrived>(void* pTarget)
А с наружи по этому указателю уже запустят саму функцию, и получат тип.
Как можно запомнить функцию?
Ведь, Derived& GetFact(void* pTarget), и NoDerived& GetFact(void* pTarget) потребуют два разных указателя.
А передача данных о функции через какое то "нечто" должно иметь единственный точный тип.
Можно было бы как нибудь запускать функции через "волшебный" указатель, которому не требуется знать о возвращаемом типе функции, тогда проблема была бы решена.
И можно было бы запросто повесить на с++ и динамику, и рефлексию.
Kartonagnick
>Можно было бы как нибудь запускать функции через "волшебный" указатель, которому не требуется знать о возвращаемом типе функции, тогда >проблема была бы решена.
К сожалению нет и связанно это с существующими моделями функций, которые оговорены.
Читай __cdecl, __stdcall, __fastcall. И тогда станет ясно почему и тогда можно будет изобретать свою модель, если не пропанет желание.
Kartonagnick
> Проблема не в том, как идентифицировать сам тип объекта. А в том, как этот
> самый тип вытащить наружу?
если идентифицировал, то приводи кастом, можно даже статическим, он прекрасно ориентируется в иерархии классов, если нет множественного наследования : )
> Как можно запомнить функцию?
указатель на функцию или "делегат", можно их хранить отдельно "стопочкой" : )
позволяет вызывать любую функцию у экземпляра любого класса с любыми аргументами (помимо тех, что заявлены в сигнатуре)
и да, в плюсах таки есть своя "магия", но не такая тупорылая, что компилятор все разруливает без знания типа, а "всемогущий" std::tr1::bind : ))
З.Ы.
Kartonagnick
> Ну це пись пись же умеет как то определять фактический тип объекта.
а позвольте спросить скока часов "налетал" на плюсах, чтобы так фамильярно? ; )
а то вона ККН с почти десятью годами опыта совсем забыл про извращённый манглинг, что выделывает компилятор чтобы хранить функции "стопочкой" в укромном месте : )
Sh.Tac
>а то вона ККН с почти десятью годами опыта совсем забыл про извращённый манглинг
я про него и не знал, а если и читал, то действительно забыл, т.к. никогда не пользовался такой фичей. Хватало и приведения типов и кастов и виртуальных методов. В редких случаях статик.
>> LuckyMan: о что должен делать newFoo.test(); если у "настоящего" типа нет такого метода?
>> Kartonagnick: LuckyMan, не о том речь, что он должен, или не должен.
С ведерком попкорна в руках терпеливо ожидаю того момента, когда топикстартера наконец озарит, что он не имеет ни малейшего понятия, что произойдет, если у объекта вызываемого метода нет, не было и не предвидится...
...и лишь один момент омрачает ожидание:
>> Kartonagnick: LuckyMan, представь себе, что у тебя есть на руках Base* ptr; Ты не знаешь его настоящего типа. Но знаешь, что у него есть метод test();
ВНЕЗАПНО, мы точно знаем, что метод есть. Иначе говоря, они все имеют по меньшей мере один общий интерфейс.
Более того, не быть метода у объекта не может в принципе. Иначе пришлось бы до вызова по тем или иным способом полученному реальному типу объекта делать выводы о правомерности вызова. Но сравнения с прописанными ручками константами (как, например, константой-типом в dynamic_cast) -- моветон и полный Содом по мнению все того же топикстартера. Выходит, проверки быть не может, а раз так, значит они все железобетонно имеют по меньшей мере один общий интерфейс, помимо Base или чего еще там. Вправе ли я предполагать, что концепция наличия нескольких интерфейсов у объекта уж очень тяжело укладывается в ваш шаблон, мсье Kartonagnick?
Хотя нет, я все-таки хочу увидеть вызов несуществующей функции. Верно было подмечено: в голову, в голову!
Cfyz
> ВНЕЗАПНО, мы точно знаем, что метод есть.
template<class T> Do(T& target) { target.Work(); }
Что бы запустить метод любого объекта, программист как минимум должен знать что именно он запускает. Компилятор "Work()" за программиста не впишет.
А вот знать его фактический тип совершенно не обязательно.
Cfyz
>Иначе говоря, они все имеют по меньшей мере один общий интерфейс.
template<class T> Do(T& target) { target.Work(); }
На кой болт любому из запускаемых таким образом объекту нужен голимый общий интерфейс? Ключевое слово "общий".
Вполне достаточно иметь свой собственный, который может быть ни с кем никак не связанный. Класс не обязан иметь родственников. Ключевое слово - "иметь".
У тебя мозги спрессованны виртуальным полиморфизмом. Но ты пока попкорн свой кушай, наблюдай. Может быть прозреешь. Может быть нет.
Kartonagnick
> А вот знать его фактический тип совершенно не обязательно.
Зависит от языка. В С++ как раз обязательно. Это в Питоне и ObjectiveC и прочих не обязательно. Только у них метаинформация о типе есть. И метаинформация добавляется не "волшебными" указателями, а на уровне транслятора.
Kartonagnick
> На кой болт любому из запускаемых таким образом объекту нужен голимый общий
> интерфейс? Ключевое слово "общий".
> Вполне достаточно иметь свой собственный, который может быть ни с кем никак не
> связанный. Класс не обязан иметь родственников. Ключевое слово - "иметь".
За структурной типизацией вы обратились не по адресу.
И за динамической тоже.
А переносить приемы языков с динамической типизацией на статически типизированые - имхо, вполне оправданно считается дурным тоном.
Grey304
> В С++ как раз обязательно.
template<class T> Do(T& target) { target.Work(); }
На кой болт программисту знать тип объекта? Вполне достаточно знать, что у объекта есть метод.
Kartonagnick
> На кой болт программисту знать тип объекта? Вполне достаточно знать, что у
> объекта есть метод.
Вы что, только с динамически типизированными языками и знакомы?
Плюсы - статически типизированный. И такой ерунды не допускают. Это, так скать, врожденное его свойство. Как и многие другие статически типизированые. Вообще, приведите примеры языков, где эта фича реализована. А то окажется, что это все динамически типизированые. (Или структрная))
А шаблоны - сущность времени компиляции. И компилятор как раз знает все в момент инстанцирования.
Тема в архиве.