1 | |||||||||||
Использование указателя на метод вместо виртуального метода20.11.2010, 12:33. Показов 5985. Ответов 45
Метки нет (Все метки)
Имеется базовый класс Base. Имеется производный от Base класс Derived. В классе Derived требуется выполнить некоторое действие, которое практически полностью эквивалентно для любого производного от Base класса, за исключением небольшого фрагмента.
Схематично код выглядит так:
0
|
20.11.2010, 12:33 | |
Ответы с готовыми решениями:
45
Вызов виртуального метода базового класса из указателя производного Почему при переопределении виртуального метода в производном классе выводится метод базового? Использовать метод transform() вместо метода sort() Как сделать функцию от указателя на класс и указателя на метод? |
270 / 176 / 46
Регистрация: 12.03.2010
Сообщений: 494
|
||||||
20.11.2010, 20:23 | 2 | |||||
Вообще решение через указатели на функции-члены выглядит устрашающе немного. А как это все будет работать зависит уже от компилятора. Можно уточнить задачу, зачем это понадобилось и почему, например, не устраивает чисто виртуальная функция с дефолтным поведением?
Да и само построение логики выглядит немного странно
0
|
21.11.2010, 00:02 [ТС] | 3 |
Потому что дефолтное не всё действие, а только часть. Для чего это нужно. Базовый класс - это некое удобное средство для реализации запуска кодов в потоке. Чтобы весь геморрой по созданию-удалению потока и синхронизации был в базовом классе, а в производных классах только задавалось действие. Tail - это "полезное" действие, которое выполняет производный класс в потоке. Exec - это "геморройное" действие по управлению потоком
0
|
270 / 176 / 46
Регистрация: 12.03.2010
Сообщений: 494
|
|
21.11.2010, 00:59 | 4 |
Наследование тут, по моему, не лучшее решение. Я буквально месяц назад писал подобную штуку, шаблоны для такого дела - самое оно. Взять тот же function или async для примера. Если нужно выполнить несколько действий, то элементарно организовывается очередь указателей на функции для выполнения, синхронизацию тоже можно запихнуть в исполняющий поток.
1
|
274 / 175 / 12
Регистрация: 14.03.2010
Сообщений: 501
|
|
21.11.2010, 01:00 | 5 |
Может, я не до конца понимаю задачу, но почему бы просто не вынести исполнение функции "Tail" из функции "Exec" базового класса в "MyExec" наследованного?
1
|
84 / 57 / 8
Регистрация: 07.08.2010
Сообщений: 185
|
||||||
21.11.2010, 09:23 | 6 | |||||
Как вариант можешь передавать в Exec функтор:
1
|
21.11.2010, 10:15 [ТС] | 7 |
Наследование приходится строить из-за того, что единственный простой способ, который я нашёл для нормальной передачи сообщения из потока в основной процесс - это через Handle окна. Мне нужно в потоке запустить некое "длительное" действие. Если это действие запускать в основной задаче, то будут тормозить (подвисать) GUI. По завершению исполнения потока я через PostMessage уведомляю окно о том, что данные готовы и надо их нарисовать на GUI. В этом месте используется Handle окна. Я бы хотел эту бодягу сделть универсальной и отрезать от окна, но тема Вопрос по PostMessage так и осталось неотвеченной, так что пока работаю так.
MyExec наследованного класса исполняется в главном процессе. Далее во время вызова Exec создаётся поток и функция Tail уже исполняется в потоке. Как уже говорилось, весь этот паровоз строится для того, чтобы реализовать простой класс для простого запуска подзадачи в потоке, а всю техническую часть по работе с потоком заключить в этом классе. К тому же хочется сделать класс условно универсальным (т.е. чтобы его можно было без дополнительных телодвижений использовать в другой программе, в том числе и в виде откомпилированной библиотеки) Я пока плохо знаком с Си++, чтобы с ходу осилить такую конструкцию. Попробую в неё вникнуть. Общий принцип понятен и выглядит логичным: при использовании указателей на функцию не будет преобразования типов, а следовательно не будет преобразования типов при передаче this - именно та опасность, о которой я писал. Скорее всего это именно то, что нужно
0
|
270 / 176 / 46
Регистрация: 12.03.2010
Сообщений: 494
|
|
21.11.2010, 10:41 | 8 |
Судя по той теме, тут можно обойтись и чистым С, винда же предоставляет стандартный пул потоков, в который можно просто пихать свои функции для исполнения (я его частенько использовал при написании небольших серверов). А для уведомление потока-обработчика можно использовать те же простенькие ивенты
0
|
21.11.2010, 10:47 [ТС] | 9 |
Для реализации потока я и так обхожусь чистым Си. Но мне нужно сделать класс для окна (формы). Чтобы потом все окна программы наследовать не от TForm, а от моего класса. И лёгким движением руки запускать нужную функциональность в потоке. Т.е. в конечном итоге реализация функциональности потока выполняется на Си++. Если бы была программа на Си без окон - я бы без вопросов работал с указателями на функцию
0
|
270 / 176 / 46
Регистрация: 12.03.2010
Сообщений: 494
|
|
21.11.2010, 11:29 | 10 |
QueueUserWorkItem - самый простой вариант( винда сама все запустит ). Ну, а в стиле С++, мне все же видится асинхронный делегат. Как окончательно проснусь - набросаю простой вариант
0
|
21.11.2010, 13:41 [ТС] | 11 | |||||
Сейчас у меня всё работает примерно по следующей схеме. Для простоты возьмём окно, которое отображает скачанную из инета картинку. На окне есть поле для картинки и кнопка. Смысл кнопки такой же, как и у браузеров. Если мы ничего не делаем, то кнопка имеет функциональность "Reload" (т.е. инициирует процесс скачивания и отображения картинки). Если идёт процесс скачивания, то кнопка имеет функциональность "Stop" (т.е. прервать текущий процесс скачивания)
Схематично примерно всё выглядит так. Здесь отображаю только код для окна с картинкой (производный класс), потому как базовый класс условно считаем чёрным ящиком, который надо реализовать. Сейчас чёрный ящик реализован через виртуальные методы
Нынешний вариант тоже работоспособен, потому как если нужно исполнять различные действия, то по дополнительному параметру можно настраивать действия внутри Execute1, ... Т.е. вопрос упирается в удобство. Вторым бонусом работы с указателями будет то, что проще организовывать работу с несколькими потоками
0
|
270 / 176 / 46
Регистрация: 12.03.2010
Сообщений: 494
|
||||||
21.11.2010, 15:59 | 12 | |||||
Вот самый простенький вариант реализации:
Добавлено через 12 минут В запуске потока код поправиль забыл
0
|
21.11.2010, 16:20 [ТС] | 13 |
В твоём коде нет самого главного - вызов события, когда поток завершил работу. В ссылке из 7-го поста я именно для того и поднял тему. По завершении работы потока мне нужно внутри класса окна обработать событие завершения. Неважно каким способом, важно лишь, чтобы это исполнялось в главном процессе. Это гарантирует мне то, что в этот момент мне не придёт никакое другое событие, а потому и не потребуется медитировать с синхронизацией
Добавлено через 13 минут Собственно, callback по завершении работы потока - это единственное, с чем я не могу нормально разобраться (а точнее, я понял, как это делать в контексте окна, но не понял, как это делать "в воздухе"). Так же хочется, чтобы callback'ом был метод класса, а не просто функция. Использование функции для callback'а действий в потоке - это механизм, на котором опирается низкоуровневое (на уровне ОС) создание потоков. И этот механизм хорошо отображается на язык Си, потому как в Си функция она и в африке функция. Чего не скажешь о Си++, ибо метод - он требует косвенного наличия this'а, который в общем случае невозможно в этот механизм включить, потому что при преобразовании this'а к void* (или любому типу, отрезанному от класса) теряется возможность вызвать метод класса. В то время как на Си любые данные можно транзитно передавать через ОС в виде void*. То, чего я хочу - это реализовать УДОБНУЮ В ИСПОЛЬЗОВАНИИ обёртку для классов. И даже что-то более узкое и конкретное: обёртку для работа в классе TForm. Конечно, в качестве callback'а можно использовать и статический метод, но такой способ влечёт за собой дополнительные телодвижения, которые в каждом конкретном случае выливаются в постоянный копипаст. Я в очередной раз специально в первом посте ставил вопрос максимально абстрактно, потому что давно знаю, что при конкретной постановке сложного вопроса в 9 случаях из 10 я услышу ответ не на свой вопрос, а на то, что мне в данный момент не интересно. Точно так же пост #12 является ответом на вопрос, который я не задавал. Хотя, несомненно, этот код является полезным материалом. Но не в контексте данного вопроса Что касается сути вопроса, то ответ я уже получил - использовать template'ы. Только надо будет это дело переварить
0
|
270 / 176 / 46
Регистрация: 12.03.2010
Сообщений: 494
|
|
21.11.2010, 16:36 | 14 |
На возможностях старого стандарта это сделать достаточно трудно, чтобы любую функцию запустить в отдельном потоке, а возможности нового стандарта пока не трогал, потому как не все еще используют поддерживающие их компиляторы. Из готовых решений есть Proactor
0
|
21.11.2010, 16:53 [ТС] | 15 |
Если внутри класса ручками создавать поток, в качестве Callback'а назначить статический метод, в качестве параметра при создании потока передать this, то всё нормально делается. За исключением того, что в каждом случае создания потока придётся повторять этот геморрой (копипаст). Мне хотелось это дело вытащить в отдельный класс, и при этом столкнулся с проблемой того, что при работе с указателями на метод теряется тип this'а. А до template'ов сам как-то не смог додуматься (потому что ни разу с ними не работал)
На мой взгляд любой начинающий должен на практике пройти этап конструирования велосипедов. Только так можно научиться делать правильно и не делать неправильно. А потом можно уже брать и готовые реализации, потому что чётко будешь понимать, что там делается, а не тупо срисовывать чужой код. Так что пока я поработаю с велосипедами
0
|
270 / 176 / 46
Регистрация: 12.03.2010
Сообщений: 494
|
||||||
21.11.2010, 17:00 | 16 | |||||
В каждом конкретном случае описывать такой себе ThreadDummy действительно просто, но сделать универсальный обьект для разнотипных функций с переменным числом параметров - тяжко на старых возможностях. Тот шаблон что я выставил можно немного переделать и сделать просто внутреннюю функцию потока, внутри которой будет вызываться переданная ей функция и колбек в конце.
0
|
21.11.2010, 17:36 [ТС] | 17 |
Такая задача не стоит. У меня Execute1, Execute2, Execute3 реализованы внутри одного класса, так что в Execute1 я записываю поля класса, а в Execute2 - читаю. Не самое красивое решение, но достаточно безгеморройное
0
|
15.04.2011, 19:49 [ТС] | 18 | |||||
Итого по ходу дела я нашёл решение для своего вопроса. Мне это нужно было для Borlnad'а, а у Borland'а есть ацкое расширнеие __closure: http://www.goforvbsix.ru/news/... -01-21-385 По ссылке текст корявый (переведён автоматическим переводчиком), но суть следующая. Если объявить указатель на функцию с модификатором __closure, то такой "указатель" будет содержать в себе на самом деле два указателя: указатель на метод и указатель на экземпляр класса в момент присваивания. Что важно, указатель на метод и указатель на экземпляр класса обезличенные: т.е. никакой информации о конкретном типе нет, а потому такую конструкцию можно протащить через транзитный код. В точке вызова по такому указателю компилятор выдерет из него адрес функции и ажрес экземпляра (по сути дела this) и по своим внутренним правилам построит вызов метода. Именно в этом месте и хранится невозможность использования данной фичи в виде обезличенных двух указателей на void, потому что после этого на языке нет возможности описать вызов метода без подпольных знаний о том, как это делает компилятор. Насколько я понимаю, подобной конструкции в языке Си++ нет, а жаль.
Я ещё не проверял, просто вывел сие из корявых описаний и объяснений на форумах. Надеюсь, что я понял всё-таки правильно Добавлено через 2 часа 3 минуты С этой конструкцией код будет выглядеть так:
0
|
Делаю внезапно и красиво
1313 / 1228 / 72
Регистрация: 22.03.2011
Сообщений: 3,744
|
|
16.04.2011, 12:45 | 19 |
А если вместо указателя на метод передавать функтор? В нём можно и this "точно правильный" передать.)
0
|
16.04.2011, 14:01 [ТС] | 20 |
Если я правильно понимаю смысл концепции функтора, то его можно использовать только тогда, когда в точке вызова известен тип. Такое можно делать в шаблонах. Но оно не годится для транзитной передачи в код, не являющийся шаблоном
Если я что-то не так понимаю, то перепиши код из поста #18 с использованием функтора. Для меня важно, чтобы внутренности класса Base НЕ являлись открытыми (т.е. не были шаблоном и не были описаны внутри описания класса)
0
|
16.04.2011, 14:01 | |
16.04.2011, 14:01 | |
Помогаю со студенческими работами здесь
20
Переопределение виртуального метода переопределение виртуального метода Ошибка создания виртуального метода? Реализовать перегрузку виртуального метода Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |