Russian Qt Forum
Ноябрь 23, 2024, 04:21 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Виртуальные функции  (Прочитано 8384 раз)
ksergey85
Гость
« : Апрель 07, 2015, 17:41 »

Здравствуйте. Возник такой вопрос. Возможно ли какими то средствами (например RTTI) вызвать виртуальный метод непосредственного предка класса объекта над этим объектом.
Поясню. Допустим имеется цепочка наследования классов A->B->C->D->... и т.д. В ходе выполнения программы мы имеем указатель на конкретный объект например класса C. Но сам указатель описан как указатель на объект базового класса A (A *obj). Допустим у класс A есть виртуальный метод, который перекрывается в каждом его предке. Пусть для определенности он называется m. Теперь, когда мы вызываем метод m нашего объекта сработает механизм виртуальных функций и в итоге будет вызван метод C::m над объектом obj. Так вот вопрос как раз в том, как вызвать над объектом obj метод B::m (ведь в таблице виртуальных функций объекта его нет), а если бы это был объект класса D, то метод C::m.
Иерархия в своей основе имеет класс QObject, и даже конструкция obj->metaObject()->superСlass() дает нам метаобъект родительского класса, но вот что с ним дальше делать ума не приложу.  Непонимающий
« Последнее редактирование: Апрель 08, 2015, 10:08 от ksergey85 » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #1 : Апрель 07, 2015, 18:07 »

Именно B::m()  и вызовет метод m из класса B.
Вы даже можете из метода класса D вызвать метод класса A.
Записан
ksergey85
Гость
« Ответ #2 : Апрель 07, 2015, 18:16 »

Вы не поняли. Связывание должно быть не статическим при компиляции, а динамическим в зависимости от того какого класса объект мы имеем, вызваться должна не его виртуальная функция а функция его непосредственного родителя. Как получить ее адрес вот в чем вопрос.
Записан
popper
Гость
« Ответ #3 : Апрель 07, 2015, 18:31 »

Иерархия в своей основе имеет класс QObject, и даже конструкция obj->metaObject()->superСlass() дает нам метаобъект родительского класса, но вот что с ним дальше делать ума не приложу.  Непонимающий
Предлагаю попробовать следующую связку:

Код
C++ (Qt)
int QMetaObject::indexOfMethod ( const char * method ) const;
QMetaMethod QMetaObject::method ( int index ) const;
bool QMetaMethod::invoke(...)
Записан
Nimbus
Гость
« Ответ #4 : Апрель 07, 2015, 18:33 »

Код
C++ (Qt)
A * a = new C();
a->A::m();
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #5 : Апрель 08, 2015, 05:40 »

Иерархия в своей основе имеет класс QObject, и даже конструкция obj->metaObject()->superСlass() дает нам метаобъект родительского класса, но вот что с ним дальше делать ума не приложу.  Непонимающий
Приводить
Код
C++ (Qt)
A * a = dynamic_cast <A *> (obj);
if (a)
a->m(); // или a->A::m() и.т.п
 
Записан
ksergey85
Гость
« Ответ #6 : Апрель 08, 2015, 09:43 »

Предлагаю попробовать следующую связку:

Код
C++ (Qt)
int QMetaObject::indexOfMethod ( const char * method ) const;
QMetaMethod QMetaObject::method ( int index ) const;
bool QMetaMethod::invoke(...)

К сожалению метод не является Q_INVOKABLE.

Код
C++ (Qt)
A * a = new C();
a->A::m();

Еще раз повторю, что статическое связывание не подойдет, т.к. я заранее не могу знать какого конкретно класса придет объект.
Записан
ksergey85
Гость
« Ответ #7 : Апрель 08, 2015, 09:45 »


Код
C++ (Qt)
A * a = dynamic_cast <A *> (obj);
if (a)
a->m(); // &#1080;&#1083;&#1080; a->A::m() &#1080;.&#1090;.&#1087;
 

И так к каждому из возможных вариантов? Тоже выход но тогда теряется универсальность написанной функции в случае если иерархия будет расширена.  Грустный
Записан
ksergey85
Гость
« Ответ #8 : Апрель 08, 2015, 09:50 »

Если взглянуть с точки зрения компилятора, то задача тривиальная - вызвать функцию с определенным адресом, передав в ecx указатель на объект. Единственная проблема состоит в том, чтобы получить адрес этой функции, то есть адрес метода класса непосредственного предка класса нашего объекта, ведь в таблице виртуальных адресов его нет. Задача как раз состоит в вычислении этого адреса.
Может быть кто-то сможет какие-нить варианты с использованием RTTI предложить? Имя класса целевого мы знаем на этапе исполнения из QMetaObject::superClass()->className(), название метода известно заранее и является статическим в принципе. Можно как то получить адрес метода имея названия класса и метода в виде двух строк?
« Последнее редактирование: Апрель 08, 2015, 09:55 от ksergey85 » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Апрель 08, 2015, 10:22 »

И так к каждому из возможных вариантов? Тоже выход но тогда теряется универсальность написанной функции в случае если иерархия будет расширена.  Грустный
Приводить придется т.к. с Ваших слов имеете указатель на QObject (который без понятия об иерархии)

Так вот вопрос как раз в том, как вызвать над объектом obj метод B::m (ведь в таблице виртуальных функций объекта его нет), а если бы это был объект класса D, то метод C::m.
Т.е. звать метод класса от которого наследуемся? Ну почему бы просто не передать это в аргументе виртуала, напр
Код
C++ (Qt)
virtual void B::m( bool useParent )
{
if (useParent) return A::m(false);
...
}
 
Или сделать еще один виртуал напр m_prev
 
Записан
ksergey85
Гость
« Ответ #10 : Апрель 08, 2015, 10:31 »

И так к каждому из возможных вариантов? Тоже выход но тогда теряется универсальность написанной функции в случае если иерархия будет расширена.  Грустный
Приводить придется т.к. с Ваших слов имеете указатель на QObject (который без понятия об иерархии)

Так вот вопрос как раз в том, как вызвать над объектом obj метод B::m (ведь в таблице виртуальных функций объекта его нет), а если бы это был объект класса D, то метод C::m.
Т.е. звать метод класса от которого наследуемся? Ну почему бы просто не передать это в аргументе виртуала, напр
Код
C++ (Qt)
virtual void B::m( bool useParent )
{
if (useParent) return A::m(false);
...
}
 
Или сделать еще один виртуал напр m_prev
 

Я ведь не спорю, что задача, которая передо мной стоит, не решается другим путём. Можно и обходную функцию создать и параметр передать. Просто решение уже не будет таким изящным. В любом языке есть свои ограничения и C++ не исключение, к сожалению.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Апрель 08, 2015, 11:05 »

Просто решение уже не будет таким изящным.
Так Вы еще и эстет Улыбающийся Ну требовать от класса отказа от собственного ф-ционала как-то не очень изящно, да и неестественно
Записан
Nimbus
Гость
« Ответ #12 : Апрель 08, 2015, 14:00 »

Еще раз повторю, что статическое связывание не подойдет, т.к. я заранее не могу знать какого конкретно класса придет объект.
Непонятно что за задачу вы решаете, но осмелюсь посоветовать прямое решение -- создать статическую мапу { typeid: boost::function<void(void)>(), ... }.
Записан
ksergey85
Гость
« Ответ #13 : Апрель 08, 2015, 15:09 »

Просто решение уже не будет таким изящным.
Так Вы еще и эстет Улыбающийся Ну требовать от класса отказа от собственного ф-ционала как-то не очень изящно, да и неестественно

Дело не в этом. Реальная задача конечно сложнее, чем та, которую я изложил здесь. Я убрал все то, что отношения к моему вопросу не имеет. Меня просто интересовало есть такая возможность у языка или нет? Я потому именно в эту ветку свой вопрос затолкал.
« Последнее редактирование: Апрель 08, 2015, 15:11 от ksergey85 » Записан
ksergey85
Гость
« Ответ #14 : Апрель 08, 2015, 15:11 »

Еще раз повторю, что статическое связывание не подойдет, т.к. я заранее не могу знать какого конкретно класса придет объект.
Непонятно что за задачу вы решаете, но осмелюсь посоветовать прямое решение -- создать статическую мапу { typeid: boost::function<void(void)>(), ... }.

А можно хотя бы чуточку поподробнее.
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.182 секунд. Запросов: 23.