1 | |||||||||||
Использование указателя на метод вместо виртуального метода20.11.2010, 12:33. Показов 5986. Ответов 45
Метки нет (Все метки)
Имеется базовый класс Base. Имеется производный от Base класс Derived. В классе Derived требуется выполнить некоторое действие, которое практически полностью эквивалентно для любого производного от Base класса, за исключением небольшого фрагмента.
Схематично код выглядит так:
0
|
20.11.2010, 12:33 | |
Ответы с готовыми решениями:
45
Вызов виртуального метода базового класса из указателя производного Почему при переопределении виртуального метода в производном классе выводится метод базового? Использовать метод transform() вместо метода sort() Как сделать функцию от указателя на класс и указателя на метод? |
Делаю внезапно и красиво
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
|
|||||||||||
16.04.2011, 14:55 | 21 | ||||||||||
А так нельзя?
0
|
16.04.2011, 15:30 [ТС] | 22 |
Конечно можно. Можно и функцией qsort не пользоваться, чтобы не работать с указателем на функцию, а ручками написать сортировку. Классом std::string тоже можно не пользоваться, а пользоваться malloc'ами и всё делать ручками. И так и так будет работать, только в одном случае интерфейсами пользоваться удобно и программу развивать несложно, а в другом - нет
0
|
Делаю внезапно и красиво
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
|
|
16.04.2011, 16:03 | 23 |
Сложность вызова даже уменьшится, т.к. вместо передачи параметра, будет "параметр" "вызываться".
Просто, не совсем ясна суть манипуляций, но тебе виднее, конечно, продолжай извращаться.)
0
|
16.04.2011, 17:14 [ТС] | 24 |
Мне кажется, что называть что-то извращением только потому, что ты чего-то не понял - это не правильно.
Общая постановка задачи. Есть чёрный ящик, в этот чёрный ящик надо отдать пару "указатель на экземпляр + указатель на метод". Вариант с классами Basi и Dervied я привёл условно, чтобы можно было описать пример того, что именно хочется сделать через указатель на метод. Для моего примера конечно же можно сделать вызов Exec, а потом вызов Tail1. Только это для упрощённой модели можно так просто сделать. Расширенный вариант выглядит так: в метод Exec тащится __closure-указатель (который есть пара "указатель на экземпляр + указатель на метод"). Внутри метода Exec создаётся поток, в потоке запускается программа, написанная на Lua, в неё передаётся моя пара, транзитно пролетает через коды на Lua и "внизу" вызывает код на Си++, который делает вызов через эту пару. Зачем так сложно? Любую сложный интерфейс, который взаимодействует с пользователем, можно написать двумя способами. Первый способ заключается в том, чтобы реализацию интерфейса сделать простой, сам интерфейс не очень удобный для пользователя и переложить все свои проблемы на плечи пользователя. Второй способ заключается в том, что реализацию интерфейса сделать сложной, но при этом получив простой интерфейс, который будет удобным в использовании. Я всегда иду по второму пути (если это возможно) Пример неудобного в использовании интерфейса: борландовский класс TThread. Чтобы им пользоваться. надо определить свой класс, который является производным классом от TThread, переопределить метод Execute, реализовать событие OnTerminate, в своём основном классе обеспечить возможность для доступа ко внутренним данным и методам (потому что часть, исполняемая в потоке, реализована в другом классе). Для маленькой программы конечно же это всё сойдёт, но когда пишешь что-то большое - то разводится очень много мусора вокруг создания потока. Оговорюсь сразу, что это не претензия к борланду: этот класс является лишь некоторым классом низкого уровня, над которым пользователь будет строить обёртку, удобную в конкретной программе. Просто пытался показать пример неудобного интерфейса и как его сделать удобным Удобным в использовании была бы надстройка над таким интерфейсом. К нашему классу (с которым мы работаем и который будет рожать поток) нам нужно добавить в родители некий базовый класс, а в момент рождения потока дёрнуть метод с прототипом типа "StartThread (f1, f2)", где f1 - это указатель на метод, который будет запущен при старте потока, f2 - указатель на метод, который будет запущен в главном процессе после завершения потока. Такой интерфейс очевидным образом использовать намного удобнее
0
|
Делаю внезапно и красиво
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
|
|||||||||||
16.04.2011, 17:23 | 25 | ||||||||||
Про "извращённость" я сказал из-за того, что ты там выше писал, что тип не известен. В (почти строго) типизированном языке избавляться от типов лично мне кажется извращением.)
Этот код делает то же самое, но без каких либо вопросов:
0
|
16.04.2011, 17:41 [ТС] | 26 | |||||
В "(почти строго) типизированном языке" НЕ избавляться от типов можно только путём раздувания кода (что мы и имеем на примере шаблонов Си++). И больше никак. Дополнительным побочным эффектом является то, что нет никакой возможности спрятать код.
Поставлю более конкретную постановку задачи. Вот такой схематичный код:
0
|
Делаю внезапно и красиво
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
|
||||||
16.04.2011, 18:08 | 27 | |||||
0
|
Делаю внезапно и красиво
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
|
|
16.04.2011, 18:20 | 29 |
Тебе же нужно после выполнения blackbox выполнить func?
При этом тип объекта неизвестен (через void*)? Как ты будешь выполнять какую-либо работу в blackbox, если тебе не известен тип объекта? Всё что ты сможешь сделать, это вызвать переданный метод func. Вот потому и непонятно мне ничего.( Чёрный ящик не пишут, его используют.)
0
|
16.04.2011, 18:27 [ТС] | 30 |
Не после, а во время
Я же несколько раз писал, что внутри чёрного ящика я не буду выполнять работу над объектом. Я этот указатель транзитно передаю и в какой-то момент внутри этого чёрного ящика вызовется переданный метод для переданного экземпляра. Но напрямую работать с экземпляром внутри чёрного ящика я не буду А кроме этого от тебя ничего и не требуется в той задаче. Только вызвать метод надо не напрямую, а из BlackBox'а
0
|
Делаю внезапно и красиво
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
|
||||||
16.04.2011, 18:41 | 31 | |||||
Вообще никаких проблем тогда нет. Но функтор нужен полиморфный. Дочерние классы в виде шаблона. Это будет самым правильным и типизированным.)
Добавлено через 2 минуты
0
|
Делаю внезапно и красиво
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
|
||||||
16.04.2011, 19:10 | 33 | |||||
В дополнение к коду выше
внутренности BlackBox без шаблонов, дополнительных классов и прочих плясок с бубнами Все пляски вокруг него. И никаких бубнов. Всё логично и просто.
0
|
16.04.2011, 23:00 [ТС] | 34 |
Какой нафиг класс Base? Какой метод Exec? Внимательно смотри пост #26 - там написаны класс A и класс B. Более того, BlackBox - это библиотечная функция, которая вообще ни о каких пользовательских классах не знает. А классы A и B - пользовательские
0
|
В астрале
8049 / 4806 / 655
Регистрация: 24.06.2010
Сообщений: 10,562
|
|
16.04.2011, 23:50 | 35 |
Evg, Используя расширения языка для конкретного компилятора писать код - плохая затея. Если тебе нужен только borland то и не парься, используй closure.
0
|
Делаю внезапно и красиво
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
|
|
17.04.2011, 07:32 | 36 |
Класс Base является базовым не для A и B, а для функтора. Иерархия A и B и прочих не изменяется. Раз BlackBox пишешь ты, то ничто не мешает заменить void* на Base*. Плюс ты получаешь бонус в виде возможности использовать различные типы и количество параметров для вызывемой метода и вызов более одного метода класса.
Библиотека ничего о пользовательских классах знать не будет, она будет знать только об интерфейсе функтора Base и всё. Добавлено через 53 секунды Я не стал писать код инициализации функтора, там просто присваивание адреса...
0
|
Модератор
8909 / 6678 / 918
Регистрация: 14.02.2011
Сообщений: 23,523
|
|
17.04.2011, 08:26 | 37 |
Evg,
еще раз повтори(много написано запутался) чем тебя не устраивает Callback? для таких вещей по моему он и создан. что значит неправильный this? Не тот класс? Не тот экземпляр? и чем не устраивают Мессаги?
0
|
17.04.2011, 10:13 [ТС] | 38 | |||||
Расширение используется только на входе и выходе цепочки. Эти классы так или иначе являются компонентами VCL. А в серединке делается транзитная передача данных, которая от расширений языка не зависит (т.е. ей дали какой-то набор байтов, и этот набор просто скопировался)
Я не парюсь. Человек не понял, а я ему объясняю. Причём объясняю не решение, а поставленный вопрос. Ты напиши исходник хотя бы схематично, а не на словах объясняй. И, как я уже неоднократно объяснял, сделай это без шаблонов Может я плохо выразился. Заставить свои коды работать я могу всегда. И всегда могу придумать с десяток решений: от идиотских до черезж...ых. В данном случае я пытаюсь найти решение, которое было бы максимально удобным для пользователя. Давай вернёмся к примеру из последнего абзаца поста #24. Постановка задачи, например, такая. Пользователь пишет класс окна (TForm). Нужно скачать из инета картинку и отобразить её на форме. Если скачивание выполнять в главном процессе, то в момент скачивания приложение будет "висеть": т.е. оно никак не будет реагировать на нажатия кнопок, не будет перерисовываться если поверх него протащить другое окно и т.п. Чтобы этого избежать, надо процесс скачивания утащить в поток. Пока картинка скачивается в потоке, приложение живёт своей жизнью: реагирует на нажатия на кнопочки, менюшки и прочие события. Как только картинка скачалась, то главный процесс каким-то образом об этом узнает и вызовет код, который отрисует картинку на форму (потому что из потока рисовать нельзя). Как такой набор действий сделать максимально удобным для пользователя? У меня работает следующий вариант. Всё то, что начинается с User - это то, что пишет пользователь, а с Lib - это библиотека. Схематично:
Если ты можешь предложить что-то более удобное для пользователя (а не для того, кто будет библиотеку поддержки реализовывать) - предложи Добавлено через 44 секунды И заодно объясни, что такое Callback Добавлено через 10 минут Да, вот ещё. Библиотека поддерживает возможность работы без потока. Т.е. LibStartThread может запустить UserDownloadImage и UserDisplayImage последовательно в главном процессе - сие удобно для процесса отладки, если возникает подозрение, что проблема обусловлена работой с потоком. Переключение между двумя режимами опять-таки делается одним лёгким движением руки
0
|
Делаю внезапно и красиво
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
|
||||||
17.04.2011, 10:40 | 39 | |||||
Я полностью исходник написал уже.) Шаблоны только для уменьшения ручного кодирования, BlackBox работает без шаблонов, т.е. с выносом в библиотеку трудностей не будет.
Я повторю код.
Если в BlackBox передавать по ссылке, то ещё проще код сделать можно. Добавлено через 2 минуты Шаблоны используются только в пользовательском коде и можно обойтись без них. Это просто облегчение жизни пользователю. BlackBox шаблоны не использует.
0
|
17.04.2011, 11:11 [ТС] | 40 |
Deviaphan, вот сравни твой код (пост 39) и мой (пост 38). В моём случае дополнительных действий раз-два и обчёлся, а в твоём случае пользователю надо писать кучу дополнительного кода. В твоём коде нет раздельного запуска двух методов по отдельности (т.е. ты в методе Execute запускаешь их одновременно). Чтобы обеспечить раздельный запуск методов, тебе придётся описывать два пользовательских класса Functor. Не говоря уж о том, что пользователь при таком раскладе свои методы ОБЯЗАН называть func1 и func2 (в противном случае работать не будет). Т.е. твой способ подразумевает перекладывание большого количества проблем с автора библиотеки на пользователя библиотеки.
Добавлено через 9 минут Либо для каждого имени метода создавать ещё один шаблон
0
|
17.04.2011, 11:11 | |
17.04.2011, 11:11 | |
Помогаю со студенческими работами здесь
40
Переопределение виртуального метода переопределение виртуального метода Ошибка создания виртуального метода? Реализовать перегрузку виртуального метода Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |