Pavel K: только учтите, что таблица виртуальных функций - статическая. Т.е. одна на все объекты класса. Поменяв указатели вы измените поведение всех объектов класса.
Pavel K: нет, т.к. вызов обычного метода происходит непосредственно по адресу. Но если вынести класс в динамическую библиотеку, то возможно подшаманить что-то в таблице импортов. Ну есть ещё хак - записать в рантайме в начало подменяемой функции jmp на нужную функцию. Но на современных ОС такой хак вряд ли получится провернуть из-за DEP - сегменты кода помечены как ro.
Pavel K: а, не правильно прочёл вопрос. Да, конечно можно. Обычный метод ничем не отличается внутри от виртуального, кроме как механизмом вызова через таблицу.
fshp: извините за наглость, можно попросить расписать как можно тогда подменить?
Вот так впринципе я получаю указатель на первую виртуальную функцию:
Class1 * cl = new Class1();
int *vptr=(int *)cl;
vptr=(int *)*vptr;
вызывать могу так:
((void (*)()) *vptr )();
указатель на другую не виртуальную функцию объявляю так:
void (Class1::* fb)();
подскажите, пожалуйста.
Pavel K: Я вам целый проектик запилил. https://github.com/fshp/vtable
Пару выводов - виртуальные функции, при вызове по ссылке, ведут себя как обычные, т.е. вызываются напрямую, минуя таблицу виртуальных функций. Значит для вашей задачи подходит только вызов по указателю.
Ну и компилировать нужно со специфичными флагами, что бы секция .text (там лежит таблица) была доступна для записи.
Таблицы виртуальных функций не являются частью стандарта и их реализация даётся на откуп разработчикам компиляторов. Значит разные компиляторы могут реализовывать их совершенно по разному. Так что код с одним компилятором может работать, а с другим - нет (даже с разными версиями одного компилятора могут быть проблемы). Я использовал gcc-4.9.
fshp: неимоверно круто, огромное спасибо! c++ торт. Если у вас есть возможность и желание за отдельную плату подсказать кое-что по реализации одного сложного #define, напишите, пожалуйста мне на почту 2me.pavelk@gmail.com
Армянское Радио: В статье разыменовывается указатель и превращается в ссылку. Вызов метода по ссылке всегда не виртуальный, т.к. приведение ссылки одного типа к другой - "undefined behavior", и тогда нет смысла лезть в таблицу, т.к. если есть ссылка - то это либо объект известного из ссылки типа (и тогда полиморфизм не нужен), либо ошибка. gcc даже с отключенными напрочь оптимизациями вызывает методы ссылки по адресу, а не через таблицу.
Армянское Радио: ну и даже с O3 моя писулька работает. Если это нужно для грязного хака на 10 минут, то можно и попробовать. В продакшне за такое убили бы на месте, это да.
Армянское Радио: что-то я совсем запутался. Вместо присвоения ссылки скопировал объект. Работает полиморфизм по ссылке, работает. Что-то меня с просони занесло :-)
Вы не понимаете. Дело в том что компилятор может заинлайнить конкретный полиморфный метод в определенный кусок кода. Отсюда следует, что подмена в виртуальной таблице для этого определенного куска кода ничего не даст. В результате автор топика получит:
#define TRUE (rand() > 0.1 ? TRUE : FALSE) // happy debugging losers
AtomKrieg: Виртуальный метод может быть заинлайнен, только если он вызывается у объекта. Не указателя или ссылки, а именно из локально созданного объекта. Если использовать ссылку или указатель виртуальный метод никогда не инлайнится.